/********************************************************************************* * 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.Map; import java.util.Map.Entry; import java.util.Vector; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.agnitas.beans.DynamicTag; import org.agnitas.beans.DynamicTagContent; import org.agnitas.beans.Mailing; import org.agnitas.beans.factory.DynamicTagContentFactory; import org.agnitas.beans.factory.MailingFactory; import org.agnitas.dao.MailingDao; import org.agnitas.dao.RecipientDao; import org.agnitas.dao.TargetDao; import org.agnitas.exceptions.CharacterEncodingValidationException; import org.agnitas.preview.PreviewHelper; import org.agnitas.preview.TAGCheck; import org.agnitas.preview.TAGCheckFactory; import org.agnitas.util.AgnUtils; import org.agnitas.util.CharacterEncodingValidator; 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; import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; /** * Implementation of <strong>Action</strong> that validates a user logon. * * @author Martin Helff */ public class MailingContentAction extends StrutsActionBase { public static final int ACTION_VIEW_CONTENT = ACTION_LAST+1; public static final int ACTION_VIEW_TEXTBLOCK = ACTION_LAST+2; public static final int ACTION_ADD_TEXTBLOCK = ACTION_LAST+3; public static final int ACTION_SAVE_TEXTBLOCK = ACTION_LAST+4; public static final int ACTION_SAVE_COMPONENT_EDIT = ACTION_LAST+5; public static final int ACTION_DELETE_TEXTBLOCK = ACTION_LAST+6; public static final int ACTION_CHANGE_ORDER_UP = ACTION_LAST+7; public static final int ACTION_CHANGE_ORDER_DOWN = ACTION_LAST+8; public static final int ACTION_CHANGE_ORDER_TOP = ACTION_LAST+9; public static final int ACTION_CHANGE_ORDER_BOTTOM = ACTION_LAST+10; public static final int ACTION_SAVE_TEXTBLOCK_AND_BACK = ACTION_LAST + 11; public static final int ACTION_MAILING_CONTENT_LAST = ACTION_LAST+11; protected TAGCheckFactory tagCheckFactory; protected CharacterEncodingValidator characterEncodingValidator; protected MailingDao mailingDao; protected MailingFactory mailingFactory; protected DynamicTagContentFactory dynamicTagContentFactory; protected TargetDao targetDao; protected RecipientDao recipientDao; // --------------------------------------------------------- Public Methods /** * 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. * <br> * ACTION_VIEW_CONTENT: loads mailing from database and puts its data into the form. Loads list of target groups * and puts it into request. Forwards to "list". * <br><br> * ACTION_VIEW_TEXTBLOCK: loads mailing from database and puts its data into the form. Loads list of target groups * and puts it into request. Forwards to "view". * <br><br> * ACTION_SAVE_TEXTBLOCK: saves content of current text module into database and forwards to content view page. * Before saving performs TagCheck validation and validation for invalid characters for given character set. * Saves new content only if no errors found. Loads list of target groups and puts it into request. Forwards * to "view". * <br><br> * ACTION_SAVE_TEXTBLOCK_AND_BACK: saves content of current text module into database and forwards to content view page. * Before saving performs TagCheck validation and validation for invalid characters for given character set. * Saves new content only if no errors found. Loads list of target groups and puts it into request. Forwards * to "list". * <br><br> * ACTION_ADD_TEXTBLOCK: creates a new content for current text module. Builds mailing dependencies and saves * whole mailing. Loads mailing into form. Forwards to content view page. * ACTION_DELETE_TEXTBLOCK: deletes selected content for current text module. Builds mailing dependencies and * saves whole mailing. Loads mailing into form. Forwards to content view page. * ACTION_CHANGE_ORDER_UP: moves content one position up in current text module. Builds mailing dependencies and * saves whole mailing. Loads mailing into form. Forwards to content view page. * ACTION_CHANGE_ORDER_DOWN: moves content one position down in current text module. Builds mailing dependencies * and saves whole mailing. Loads mailing into form. Forwards to content view page. * ACTION_CHANGE_ORDER_TOP: moves content on top in current text module. Builds mailing dependencies and saves * whole mailing. Loads mailing into form. Forwards to content view page. * ACTION_CHANGE_ORDER_BOTTOM: moves content on bottom in current text module. Builds mailing dependencies and saves * whole mailing. Loads mailing into form. Forwards to content view page. * <br><br> * If destination is "list" - loads list of admin- and test-recipients of current mailing to request (is needed * for live preview). * * @param form data for the action filled by the jsp * @param req request from jsp * @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 specified in struts-config.xml to forward to next jsp */ public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { // Validate the request parameters specified by the user MailingContentForm aForm=null; ActionMessages errors = new ActionMessages(); ActionMessages messages = new ActionMessages(); ActionForward destination=null; boolean hasErrors; List<String[]> errorReport; if(!AgnUtils.isUserLoggedIn(req)) { return mapping.findForward("logon"); } aForm = (MailingContentForm) form; AgnUtils.logger().info("Action: " + aForm.getAction()); if (!allowed("mailing.content.show", req)) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); saveErrors(req, errors); return null; } try { switch(aForm.getAction()) { case MailingContentAction.ACTION_VIEW_CONTENT: loadMailing(aForm, req); destination=mapping.findForward("list"); break; case MailingContentAction.ACTION_VIEW_TEXTBLOCK: aForm.setAction(MailingContentAction.ACTION_SAVE_TEXTBLOCK); loadMailing(aForm, req); destination=mapping.findForward("view"); break; case MailingContentAction.ACTION_SAVE_TEXTBLOCK: hasErrors = false; errorReport = new ArrayList<String[]>(); if( aForm.getMailingID() != 0 ) { Map<String, DynamicTagContent> contentMap = aForm.getContent(); TAGCheck tagCheck = tagCheckFactory.createTAGCheck(aForm.getMailingID()); for(Entry<String,DynamicTagContent> entry:contentMap.entrySet()) { StringBuffer tagErrorReport = new StringBuffer(); Vector<String> failures = new Vector<String>(); if( !tagCheck.checkContent(entry.getValue().getDynContent(),tagErrorReport, failures)) { appendErrorsToList(entry.getValue().getDynName(), errorReport, tagErrorReport); hasErrors = true; } } tagCheck.done(); } if( !hasErrors ){ performContentValidation(aForm, errors); try { this.saveContent(aForm, req); } catch (TransientDataAccessResourceException e) { AgnUtils.logger().error("execute: " + e + "\n" + AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.hibernate.attachmentTooLarge")); } aForm.setAction(MailingContentAction.ACTION_SAVE_TEXTBLOCK); loadMailing(aForm, req); // Show "changes saved" if (errors.isEmpty()) { messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } } else { req.setAttribute("errorReport", errorReport); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.template.dyntags")); } putTargetGroupsInRequest(req); destination=mapping.findForward("view"); break; case MailingContentAction.ACTION_SAVE_TEXTBLOCK_AND_BACK: hasErrors = false; errorReport = new ArrayList<String[]>(); if( aForm.getMailingID() != 0 ) { Map<String, DynamicTagContent> contentMap = aForm.getContent(); TAGCheck tagCheck = tagCheckFactory.createTAGCheck(aForm.getMailingID()); for(Entry<String,DynamicTagContent> entry:contentMap.entrySet()) { StringBuffer tagErrorReport = new StringBuffer(); Vector<String> failures = new Vector<String>(); if( !tagCheck.checkContent(entry.getValue().getDynContent(),tagErrorReport, failures)) { appendErrorsToList(entry.getValue().getDynName(), errorReport, tagErrorReport); hasErrors = true; } } tagCheck.done(); } if( !hasErrors ){ performContentValidation(aForm, errors); try { this.saveContent(aForm, req); } catch (TransientDataAccessResourceException e) { AgnUtils.logger().error("execute: " + e + "\n" + AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.hibernate.attachmentTooLarge")); } aForm.setAction(MailingContentAction.ACTION_SAVE_TEXTBLOCK); loadMailing(aForm, req); // Show "changes saved" if (errors.isEmpty()) { messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } } else { req.setAttribute("errorReport", errorReport); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.template.dyntags")); } putTargetGroupsInRequest(req); destination=mapping.findForward("list"); break; case MailingContentAction.ACTION_ADD_TEXTBLOCK: case MailingContentAction.ACTION_DELETE_TEXTBLOCK: case MailingContentAction.ACTION_CHANGE_ORDER_UP: case MailingContentAction.ACTION_CHANGE_ORDER_DOWN: case MailingContentAction.ACTION_CHANGE_ORDER_TOP: case MailingContentAction.ACTION_CHANGE_ORDER_BOTTOM: destination=mapping.findForward("view"); try { this.saveContent(aForm, req); } catch (TransientDataAccessResourceException e) { AgnUtils.logger().error("execute: " + e + "\n" + AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.hibernate.attachmentTooLarge")); } aForm.setAction(MailingContentAction.ACTION_VIEW_TEXTBLOCK); loadMailing(aForm, req); if (errors.isEmpty()) { messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } break; } } catch (Exception e) { AgnUtils.logger().error("execute: "+e+"\n"+AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.exception")); } if(destination != null && "list".equals(destination.getName())) { putPreviewRecipientsInRequest(req, aForm.getMailingID(),aForm.getCompanyID(req), recipientDao); } // 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; } protected void performContentValidation(MailingContentForm form, ActionMessages errors){ try { String emailParameter = mailingDao.getEmailParameter(form.getMailingID()); if (emailParameter != null) { String charset = AgnUtils.getAttributeFromParameterString(emailParameter, "charset"); characterEncodingValidator.validate(form, charset); } } catch (CharacterEncodingValidationException e) { for (String mailingComponent : e.getFailedMailingComponents()) errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.charset.component", mailingComponent)); for (String dynTag : e.getFailedDynamicTags()) errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.charset.content", dynTag)); } } /** * Loads mailing. */ protected Mailing loadMailing(MailingContentForm aForm, HttpServletRequest req) throws Exception { Mailing aMailing=mailingDao.getMailing(aForm.getMailingID(), this.getCompanyID(req)); if(aMailing==null) { aMailing=mailingFactory.newMailing(); aMailing.init(getCompanyID(req), getApplicationContext(req)); aMailing.setId(0); aForm.setMailingID(0); } aForm.setShortname(aMailing.getShortname()); aForm.setDescription(aMailing.getDescription()); aForm.setIsTemplate(aMailing.isIsTemplate()); aForm.setMailinglistID(aMailing.getMailinglistID()); aForm.setMailingID(aMailing.getId()); aForm.setMailFormat(aMailing.getEmailParam().getMailFormat()); aForm.setTags(aMailing.getDynTags(), true); aForm.setWorldMailingSend(aMailing.isWorldMailingSend()); if(aForm.getAction()==MailingContentAction.ACTION_VIEW_TEXTBLOCK || aForm.getAction()==MailingContentAction.ACTION_SAVE_TEXTBLOCK) { aForm.setContent(aMailing.getDynamicTagById(aForm.getDynNameID()).getDynContent()); String dynTargetName = aMailing.getDynamicTagById(aForm.getDynNameID()).getDynName(); boolean showHTMLEditor = calculateShowingHTMLEditor(dynTargetName, aMailing, req); aForm.setShowHTMLEditor(showHTMLEditor); aForm.setDynName(dynTargetName); } String entityName = aMailing.isIsTemplate() ? "template" : "mailing"; AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": do load " + entityName + " " + aMailing.getShortname()); putTargetGroupsInRequest(req); return aMailing; } protected boolean calculateShowingHTMLEditor(String dynTargetName, Mailing aMailing, HttpServletRequest req) throws Exception { String htmlEmmBlock = aMailing.getHtmlTemplate().getEmmBlock(); Vector<String> tagsInHTMLTemplate = aMailing.findDynTagsInTemplates(htmlEmmBlock, getApplicationContext(req)); if (tagsInHTMLTemplate.contains(dynTargetName)) { return true; } return false; } /** * Saves content. * @throws CharacterEncodingValidationException */ protected void saveContent(MailingContentForm aForm, HttpServletRequest req) throws CharacterEncodingValidationException { Mailing aMailing=mailingDao.getMailing(aForm.getMailingID(), this.getCompanyID(req)); DynamicTagContent aContent=null; if(aMailing!=null) { DynamicTag aTag=aMailing.getDynamicTagById(aForm.getDynNameID()); if(aTag!=null) { aTag.setDynContent(aForm.getContent()); switch(aForm.getAction()) { case MailingContentAction.ACTION_ADD_TEXTBLOCK: aContent=dynamicTagContentFactory.newDynamicTagContent(); aContent.setCompanyID(this.getCompanyID(req)); aContent.setDynContent(aForm.getNewContent()); aContent.setTargetID(aForm.getNewTargetID()); aContent.setDynOrder(aTag.getMaxOrder()+1); aContent.setDynNameID(aTag.getId()); aContent.setMailingID(aTag.getMailingID()); aTag.addContent(aContent); break; case MailingContentAction.ACTION_DELETE_TEXTBLOCK: // reload mailing to get a persisted dynContent table aMailing=mailingDao.getMailing(aForm.getMailingID(), this.getCompanyID(req)); aTag=aMailing.getDynamicTagById(aForm.getDynNameID()); aTag.removeContent(aForm.getContentID()); break; case MailingContentAction.ACTION_CHANGE_ORDER_UP: // aTag.changeContentOrder(aForm.getContentID(), 1); aTag.moveContentDown(aForm.getContentID(), -1); break; case MailingContentAction.ACTION_CHANGE_ORDER_DOWN: aTag.moveContentDown(aForm.getContentID(), 1); // aTag.changeContentOrder(aForm.getContentID(), 2); break; case MailingContentAction.ACTION_CHANGE_ORDER_TOP: for (int numOfContent = 0; numOfContent < aTag.getDynContentCount(); numOfContent++) { aTag.moveContentDown(aForm.getContentID(), -1); } break; case MailingContentAction.ACTION_CHANGE_ORDER_BOTTOM: for (int numOfContent = 0; numOfContent < aTag.getDynContentCount(); numOfContent++) { aTag.moveContentDown(aForm.getContentID(), 1); } break; } } try { aMailing.buildDependencies(false, getApplicationContext(req)); } catch (Exception e) { AgnUtils.logger().error(e.getMessage()); AgnUtils.logger().error(AgnUtils.getStackTrace(e)); } mailingDao.saveMailing(aMailing); // save String entityName = aMailing.isIsTemplate() ? "template" : "mailing"; switch (aForm.getAction()) { case MailingContentAction.ACTION_ADD_TEXTBLOCK: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": create textblock " + aContent.getId() + " in to " + entityName + " " + aMailing.getShortname()); break; case MailingContentAction.ACTION_DELETE_TEXTBLOCK: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": delete textblock " + aForm.getContentID() + " from " + entityName + " " + aMailing.getShortname()); break; case MailingContentAction.ACTION_CHANGE_ORDER_UP: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": do order up textblock " + aForm.getContentID() + " from " + entityName + " " + aMailing.getShortname()); break; case MailingContentAction.ACTION_CHANGE_ORDER_DOWN: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": do order down textblock " + aForm.getContentID() + " from "+ entityName + " " + aMailing.getShortname()); break; case MailingContentAction.ACTION_CHANGE_ORDER_TOP: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": do order top textblock " + aForm.getContentID() + " from "+ entityName + " " + aMailing.getShortname()); break; case MailingContentAction.ACTION_CHANGE_ORDER_BOTTOM: AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": do order bottom textblock " + aForm.getContentID() + " from " + entityName + " " + aMailing.getShortname()); break; } } AgnUtils.logger().info("change content of mailing: "+aForm.getMailingID()); } protected void appendErrorsToList(String blockName, List<String[]> errorReports, StringBuffer templateReport) { Map<String,String> tagsWithErrors = PreviewHelper.getTagsWithErrors(templateReport); for(Entry<String,String> entry:tagsWithErrors.entrySet()) { String[] errorRow = new String[3]; errorRow[0] = blockName; // block errorRow[1] = entry.getKey(); // tag errorRow[2] = entry.getValue(); // value errorReports.add(errorRow); } List<String> errorsWithoutATag = PreviewHelper.getErrorsWithoutATag(templateReport); for(String error:errorsWithoutATag){ String[] errorRow = new String[3]; errorRow[0] = blockName; errorRow[1] = ""; errorRow[2] = error; errorReports.add(errorRow); } } @Override protected void putTargetGroupsInRequest(HttpServletRequest req) { req.setAttribute("targetGroups", targetDao.getTargets(this.getCompanyID(req), true)); } protected WebApplicationContext getApplicationContext(HttpServletRequest req){ return WebApplicationContextUtils.getRequiredWebApplicationContext(req.getSession().getServletContext()); } public void setTagCheckFactory(TAGCheckFactory tagCheckFactory) { this.tagCheckFactory = tagCheckFactory; } public void setCharacterEncodingValidator(CharacterEncodingValidator characterEncodingValidator) { this.characterEncodingValidator = characterEncodingValidator; } public void setMailingDao(MailingDao mailingDao) { this.mailingDao = mailingDao; } public void setMailingFactory(MailingFactory mailingFactory) { this.mailingFactory = mailingFactory; } public void setDynamicTagContentFactory(DynamicTagContentFactory dynamicTagContentFactory) { this.dynamicTagContentFactory = dynamicTagContentFactory; } public void setTargetDao(TargetDao targetDao) { this.targetDao = targetDao; } public void setRecipientDao(RecipientDao recipientDao) { this.recipientDao = recipientDao; } }