/********************************************************************************* * The contents of this file are subject to the Common Public Attribution * License Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.openemm.org/cpal1.html. The License is based on the Mozilla * Public License Version 1.1 but Sections 14 and 15 have been added to cover * use of software over a computer network and provide for limited attribution * for the Original Developer. In addition, Exhibit A has been modified to be * consistent with Exhibit B. * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is OpenEMM. * The Original Developer is the Initial Developer. * The Initial Developer of the Original Code is AGNITAS AG. All portions of * the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights * Reserved. * * Contributor(s): AGNITAS AG. ********************************************************************************/ package org.agnitas.web; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.agnitas.beans.ProfileField; import org.agnitas.beans.factory.ProfileFieldFactory; import org.agnitas.dao.ProfileFieldDao; import org.agnitas.dao.TargetDao; import org.agnitas.service.ColumnInfoService; import org.agnitas.target.Target; import org.agnitas.target.TargetNode; import org.agnitas.util.AgnUtils; import org.agnitas.util.DbUtilities; import org.agnitas.util.KeywordList; import org.agnitas.util.SafeString; import org.apache.log4j.Logger; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; /** * Handles all actions on profile fields. */ public class ProfileFieldAction extends StrutsActionBase { private static final transient Logger logger = Logger.getLogger(ProfileFieldAction.class); // --------------------------------------------------------- Dependency Injection code protected ProfileFieldFactory profileFieldFactory; protected ColumnInfoService columnInfoService; protected KeywordList databaseKeywordList; protected ProfileFieldDao profileFieldDao; protected TargetDao targetDao; protected ExecutorService workerExecutorService; public void setProfileFieldFactory(ProfileFieldFactory factory) { this.profileFieldFactory = factory; } public void setColumnInfoService(ColumnInfoService service) { this.columnInfoService = service; } public void setDatabaseKeywordList(KeywordList keywordList) { this.databaseKeywordList = keywordList; } public void setProfileFieldDao(ProfileFieldDao profileFieldDao) { this.profileFieldDao = profileFieldDao; } public void setTargetDao(TargetDao targetDao) { this.targetDao = targetDao; } public void setWorkerExecutorService(ExecutorService executorService) { this.workerExecutorService = executorService; } // --------------------------------------------------------- Public Methods private String[] HIDDEN_COLUMNS = {"change_date", "creation_date", "title", "datasource_id", "email", "firstname", "lastname", "gender", "mailtype", "customer_id", "timestamp", "bounceload"}; /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * * ACTION_LIST: Forwards to list page. * <br><br> * ACTION_VIEW: if request parameter "fieldname" is set - loads field data into form, otherwise - sets default * field type to double; forwards to profile field view page. * <br><br> * ACTION_SAVE: if reserved word is used for field name or if the error occurred during save - adds error and * forwards to view page. If everything is ok - updates existing field in database and forwards to list page; * <br><br> * ACTION_NEW: if there is parameter "save" in request and new field name is not a reserved word and the * shortname or fieldname is not duplicated - creates new profile field in database and forwards to list page. * If there some error occurred - forwards to view page. If the reserved word is used for field name - forwards * to list page. * <br><br> * ACTION_CONFIRM_DELETE: loads profile field data into form. Checks if the field is used by any target group: * if the fields is unused - forwards to delete-confirmation page, if the field is used - stores the targets * names using this field to form and forwards to "delete_error" page. If there was an error during targets * check - forwards to list page. * <br><br> * ACTION_DELETE: deletes the profile field from database, forwards to profile field list page. * <br><br> * Any other ACTION_* would cause a forward to "list" * <br><br> * If the destination is "list" - gets list of profile fields excluding hidden fields and sets that into request. * * @param form ActionForm object * @param req request * @param res response * @param mapping The ActionMapping used to select this instance * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs * @return destination */ @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException, Exception { // Validate the request parameters specified by the user ProfileFieldForm aForm = null; ActionMessages errors = new ActionErrors(); ActionMessages messages = new ActionMessages(); ActionForward destination = null; if (!AgnUtils.isUserLoggedIn(req)) { return mapping.findForward("logon"); } aForm = (ProfileFieldForm)form; if (req.getParameter("delete") != null) { aForm.setAction(ACTION_CONFIRM_DELETE); } if (logger.isInfoEnabled()) logger.info("Action: "+aForm.getAction()); try { switch(aForm.getAction()) { case ProfileFieldAction.ACTION_LIST: if (allowed("profileField.show", req)) { destination = mapping.findForward("list"); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; case ProfileFieldAction.ACTION_VIEW: if (allowed("profileField.show", req)) { if (req.getParameter("fieldname") != null) { loadProfileField(aForm, req); aForm.setAction(ProfileFieldAction.ACTION_SAVE); } else { aForm.setAction(ProfileFieldAction.ACTION_NEW); // For creating a new field set default values aForm.setFieldType("DOUBLE"); } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } destination = mapping.findForward("view"); break; case ProfileFieldAction.ACTION_SAVE: if (req.getParameter("save") != null) { if (isReservedWord(aForm.getFieldname())) { destination = mapping.findForward("view"); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.profiledb.invalid_fieldname", aForm.getFieldname())); } else if (changeProfileField(aForm, req, errors)){ destination = mapping.findForward("list"); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("changes_saved")); } else { // Show error destination = mapping.findForward("view"); if (errors.isEmpty()) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.profiledb.invalid_fieldname", aForm.getFieldname())); } } } break; case ProfileFieldAction.ACTION_NEW: if (allowed("profileField.show", req)) { if (req.getParameter("save") != null) { if (!isReservedWord(aForm.getFieldname())) { if (newProfileField(aForm, req, errors)){ aForm.setAction(ProfileFieldAction.ACTION_LIST); destination = mapping.findForward("list"); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("changes_saved")); } else { // error message: NewProfileDBFieldError: errors.add("NewProfileDB_Field", new ActionMessage("error.profiledb.insert_in_db_error")); destination = mapping.findForward("view"); } } else { destination = mapping.findForward("view"); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.profiledb.invalid_fieldname", aForm.getFieldname())); } } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; case ProfileFieldAction.ACTION_CONFIRM_DELETE: loadProfileField(aForm, req); String tg_ret = checkForTargetGroups(aForm, req); if (tg_ret.compareTo("ok") == 0 ) { aForm.setAction(ProfileFieldAction.ACTION_DELETE); destination = mapping.findForward("delete"); } else { if (tg_ret.compareTo("error") != 0 ) { aForm.setAction(ProfileFieldAction.ACTION_LIST); aForm.setTargetsDependent(tg_ret); destination = mapping.findForward("delete_error"); } else { aForm.setAction(ProfileFieldAction.ACTION_LIST); destination = mapping.findForward("list"); } } break; case ProfileFieldAction.ACTION_DELETE: if (req.getParameter("kill") != null) { deleteProfileField(aForm, req); aForm.setAction(ProfileFieldAction.ACTION_LIST); destination = mapping.findForward("list"); messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } break; default: aForm.setAction(ProfileFieldAction.ACTION_LIST); if (allowed("profileField.show", req)) { destination = mapping.findForward("list"); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } } } catch (Exception e) { logger.error("execute: "+e+"\n"+AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.exception")); throw new ServletException(e); } if (destination != null && "list".equals(destination.getName())) { prepareList(mapping, req, errors, destination, aForm); } // Report any errors we have discovered back to the original form if (!errors.isEmpty()) { saveErrors(req, errors); } // Report any message (non-errors) we have discovered if (!messages.isEmpty()) { saveMessages(req, messages); } return destination; } /** * Loads a profile field. */ protected void loadProfileField(ProfileFieldForm aForm, HttpServletRequest req) { int compID = AgnUtils.getAdmin(req).getCompany().getId(); ProfileField profileField = null; try { profileField = columnInfoService.getColumnInfo(compID, aForm.getFieldname()); } catch(Exception e) { logger.error(e.getMessage(), e); return; } aForm.setCompanyID(compID); if (profileField != null) { aForm.setShortname(profileField.getShortname()); aForm.setDescription(profileField.getDescription()); aForm.setFieldType(profileField.getDataType()); aForm.setFieldLength(profileField.getDataTypeLength()); aForm.setFieldDefault(profileField.getDefaultValue()); aForm.setFieldNull(profileField.getNullable()); } else { aForm.setShortname(""); aForm.setDescription(""); aForm.setFieldType(""); aForm.setFieldLength(0); aForm.setFieldDefault(""); aForm.setFieldNull(true); } } /** * Creates a profile field. * @param errors */ protected boolean newProfileField(ProfileFieldForm aForm, HttpServletRequest req, ActionMessages errors) throws Exception { int companyID = AgnUtils.getAdmin(req).getCompany().getId(); String columnName = aForm.getFieldname(); String shortName = aForm.getShortname(); // check if columnname or shortname is already in use if (profileFieldDao.getProfileFieldByShortname(companyID, shortName) != null ) { errors.add("settings.NewProfileDB_Field", new ActionMessage("error.profiledb.exists")); return false; } else if (profileFieldDao.getProfileField(companyID, columnName) != null) { errors.add("settings.NewProfileDB_Field", new ActionMessage("error.profiledb.exists")); return false; } else if (!DbUtilities.checkAllowedDefaultValue(aForm.getFieldType(), aForm.getFieldDefault())) { errors.add("settings.NewProfileDB_Field", new ActionMessage("error.profiledb.invalidDefaultValue")); return false; } else { ProfileField field = profileFieldFactory.newProfileField(); field.setCompanyID(companyID); field.setColumn(columnName); field.setShortname(SafeString.getSQLSafeString(shortName)); field.setDescription(SafeString.getSQLSafeString(aForm.getDescription())); field.setDataType(aForm.getFieldType()); field.setDataTypeLength(aForm.getFieldLength()); field.setDefaultValue(SafeString.getSQLSafeString(aForm.getFieldDefault())); try { return profileFieldDao.saveProfileField(field); } catch (Exception e) { return false; } } } /** * Changes a profile field. * @param errors */ protected boolean changeProfileField(ProfileFieldForm aForm, HttpServletRequest req, ActionMessages errors) throws Exception { int companyID = AgnUtils.getAdmin(req).getCompany().getId(); String columnName = aForm.getFieldname(); // check if columnname exists ProfileField existingProfileField = profileFieldDao.getProfileField(companyID, columnName); if (existingProfileField == null) { errors.add("ChangeProfileDB_Field", new ActionMessage("error.profiledb.notExists")); return false; } else if (!DbUtilities.checkAllowedDefaultValue(existingProfileField.getDataType(), aForm.getFieldDefault())) { errors.add("ChangeProfileDB_Field", new ActionMessage("error.profiledb.invalidDefaultValue")); return false; } else { existingProfileField.setDataType(aForm.getFieldType()); existingProfileField.setDataTypeLength(aForm.getFieldLength()); existingProfileField.setDescription(SafeString.getSQLSafeString(aForm.getDescription())); existingProfileField.setShortname(SafeString.getSQLSafeString(aForm.getShortname())); existingProfileField.setDefaultValue(SafeString.getSQLSafeString(aForm.getFieldDefault())); try { profileFieldDao.saveProfileField(existingProfileField); return true; } catch (Exception e) { return false; } } } /** * Removes a profile field. * @throws Exception */ protected void deleteProfileField(ProfileFieldForm aForm, HttpServletRequest req) throws Exception { String fieldname = SafeString.getSQLSafeString(aForm.getFieldname()); try { profileFieldDao.removeProfileField(AgnUtils.getAdmin(req).getCompany().getId(), fieldname); } catch (Exception e) { e.printStackTrace(); } } /** * Checks for target groups. */ protected String checkForTargetGroups(ProfileFieldForm aForm, HttpServletRequest req) { int compID = AgnUtils.getAdmin(req).getCompany().getId(); String fieldname = aForm.getFieldname(); StringBuilder ids = new StringBuilder(); List<Target> targets = targetDao.getTargets(compID); List<String> targetNamesFound = new ArrayList<String>(); for (Target aTarget : targets) { if (aTarget != null && aTarget.getTargetStructure() != null) { for (TargetNode aNode : aTarget.getTargetStructure().getAllNodes()) { if (aNode.getPrimaryField().equals(fieldname)) { if (!targetNamesFound.contains(aTarget.getTargetName())) { targetNamesFound.add(aTarget.getTargetName()); if (ids.length() > 0) { ids.append("<br>"); } ids.append(aTarget.getTargetName()); } } } } } if (ids.length() > 0) { return ids.toString(); } else { return "ok"; } } protected ActionForward prepareList(ActionMapping mapping, HttpServletRequest req, ActionMessages errors, ActionForward destination,ProfileFieldForm profileFieldForm) { try { List<ProfileField> profileFieldList = columnInfoService.getColumnInfos(AgnUtils.getCompanyID(req)); List<ProfileField> columnInfoListFiltered = new ArrayList<ProfileField>(); for (ProfileField comProfileField : profileFieldList) { boolean hideColumn = false; for (String hiddenColumn : HIDDEN_COLUMNS) { if (hiddenColumn.equalsIgnoreCase(comProfileField.getColumn())) { hideColumn = true; } } if (!hideColumn) { columnInfoListFiltered.add(comProfileField); } } req.setAttribute("columnInfo", columnInfoListFiltered); } catch (Exception e) { AgnUtils.userlogger().error("execute: " + e + "\n" + AgnUtils.getStackTrace(e)); } return mapping.findForward("list"); } protected boolean isReservedWord(String word) { return databaseKeywordList.containsKeyWord(word); } }