HTML Mail Action in alfresco(3.4d)

Just a few days before I was having requirement for sending notification mail which contains HTML text in alfresco.Alfresco version in which i have to do this was 3.4d.Sending mail in alfresco is not much difficult, as there are many things all ready created for us for example custom java action for sending mail.
We only need to add parameters like mail text , subject ,receiver etc...We can simply achieve this by below code.

var mail = actions.create("mail");
mail.parameters.to = "krutik.jayswal@gmail.com";
mail.parameters.subject = "Html test";
mail.parameters.from = "alfresco.architect@mail.com";
mail.parameters.text = "My name is Krutik Jayswal if you see this it is very bold";
mail.parameters.node=document;
mail.execute(document);

But above was not working in version below alfresco 3.4d.Alfresco version <=3.4d does not supports html mail content.So for this we need to create one custom action which supports html mail text.For that you need to follow below steps.


Step 1 : Create Custom Html Action
Below is Java class for that.Save it as HTMLMailFormatter.java.I did little customization in the in built mail action.
/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package com.mail;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.action.executer.TestModeable;
import org.alfresco.repo.template.DateCompareMethod;
import org.alfresco.repo.template.HasAspectMethod;
import org.alfresco.repo.template.I18NMessageMethod;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.EmailValidator;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;

/**
 * Mail action executor implementation.
 * 
 * @author Roy Wetherall
 */
public class HTMLMailFormatter extends ActionExecuterAbstractBase
    implements InitializingBean, TestModeable
{
    private static Log logger = LogFactory.getLog(HTMLMailFormatter.class);
    
    /**
     * Action executor constants
     */
    public static final String NAME = "mail";
    public static final String PARAM_TO = "to";
    public static final String PARAM_TO_MANY = "to_many";
    public static final String PARAM_SUBJECT = "subject";
    public static final String PARAM_TEXT = "text";
    public static final String PARAM_FROM = "from";
    public static final String PARAM_TEMPLATE = "template";
    public static final String IS_HTML_SUPPORTED= "html";
       
    /**
     * From address
     */
    private static final String FROM_ADDRESS = "alfresco@alfresco.org";
    
    private static final String REPO_REMOTE_URL = "http://localhost:8080/alfresco";
    
    /**
     * The java mail sender
     */
    private JavaMailSender javaMailSender;
    
    /**
     * The Template service
     */
    private TemplateService templateService;
    
    /**
     * The Person service
     */
    private PersonService personService;
    
    /**
     * The Authentication service
     */
    private AuthenticationService authService;
    
    /**
     * The Node Service
     */
    private NodeService nodeService;
    
    /**
     * The Authority Service
     */
    private AuthorityService authorityService;
    
    /**
     * The Service registry
     */
    private ServiceRegistry serviceRegistry;
    
    /**
     * Mail header encoding scheme
     */
    private String headerEncoding = null;
    
    /**
     * Default from address
     */
    private String fromAddress = null;
    
    /**
     * Default alfresco installation url
     */
    private String repoRemoteUrl = null;
    
    private boolean sendTestMessage = false;
    private String testMessageTo = null;
    private String testMessageSubject = "Test message";
    private String testMessageText = "This is a test message.";

    /**
     * Test mode prevents email messages from being sent.
     * It is used when unit testing when we don't actually want to send out email messages.
     * 
     * MER 20/11/2009 This is a quick and dirty fix. It should be replaced by being 
     * "mocked out" or some other better way of running the unit tests. 
     */
    private boolean testMode = false;
    private MimeMessage lastTestMessage;
    
    /**
     * @param javaMailSender    the java mail sender
     */
    public void setMailService(JavaMailSender javaMailSender) 
    {
        this.javaMailSender = javaMailSender;
    }
    
    /**
     * @param templateService   the TemplateService
     */
    public void setTemplateService(TemplateService templateService)
    {
        this.templateService = templateService;
    }
    
    /**
     * @param personService     the PersonService
     */
    public void setPersonService(PersonService personService)
    {
        this.personService = personService;
    }
    
    /**
     * @param authService       the AuthenticationService
     */
    public void setAuthenticationService(AuthenticationService authService)
    {
        this.authService = authService;
    }
    
    /**
     * @param serviceRegistry   the ServiceRegistry
     */
    public void setServiceRegistry(ServiceRegistry serviceRegistry)
    {
        this.serviceRegistry = serviceRegistry;
    }
    
    /**
     * @param authorityService  the AuthorityService
     */
    public void setAuthorityService(AuthorityService authorityService)
    {
        this.authorityService = authorityService;
    }

    /**
     * @param nodeService       the NodeService to set.
     */
    public void setNodeService(NodeService nodeService)
    {
        this.nodeService = nodeService;
    }
    
    /**
     * @param headerEncoding     The mail header encoding to set.
     */
    public void setHeaderEncoding(String headerEncoding)
    {
        this.headerEncoding = /*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

package org.alfresco.repo.invitation.site.ds;

import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.*;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.search.SearcherException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.invitation.InvitationException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.springframework.extensions.surf.util.URLEncoder;

/**
 * This class is responsible for sending email invitations, allowing nominated
 * user's to join a Site.
 * 
 * @author Nick Smith
 */
public class InviteSender
{
    public static final String WF_INSTANCE_ID = "wf_instanceId";
    public static final String WF_PACKAGE = "wf_package";

    private static final List<String> expectedProperties = Arrays.asList(wfVarInviteeUserName,//
                wfVarResourceName,//
                wfVarInviterUserName,//
                wfVarInviteeUserName,//
                wfVarRole,//
                wfVarInviteeGenPassword,//
                wfVarResourceName,//
                wfVarInviteTicket,//
                wfVarServerPath,//
                wfVarAcceptUrl,//
                wfVarRejectUrl, WF_INSTANCE_ID,//
                WF_PACKAGE);
    public static final String IS_HTML="IS_HTML";
    public static final String MAILHTMLACTION="mailHtmlAction";
    private final ActionService actionService;
    private final NodeService nodeService;
    private final PersonService personService;
    private final SearchService searchService;
    private final SiteService siteService;
    private final TemplateService templateService;
    private final Repository repository;
    private final MessageService messageService;
    
    public InviteSender(ServiceRegistry services, Repository repository, MessageService messageService)
    {
        this.actionService = services.getActionService();
        this.nodeService = services.getNodeService();
        this.personService = services.getPersonService();
        this.searchService = services.getSearchService();
        this.siteService = services.getSiteService();
        this.templateService = services.getTemplateService();
        this.repository = repository;
        this.messageService = messageService;
    }

    /**
     * Sends an invitation email.
     * 
     * @param properties A Map containing the properties needed to send the
     *            email.
     */
    public void sendMail(Map<String, String> properties)
    {
        checkProperties(properties);
        ParameterCheck.mandatory("Properties", properties);
        NodeRef inviter = personService.getPerson(properties.get(wfVarInviterUserName));
        String inviteeName = properties.get(wfVarInviteeUserName);
        NodeRef invitee = personService.getPerson(inviteeName);
        Action mail = null;
        if(properties.get(InviteSender.IS_HTML).equals("true")){
         mail=actionService.createAction(MAILHTMLACTION);
            mail.setParameterValue("html",true);
        }else{
         mail=actionService.createAction(MailActionExecuter.NAME);
        }
        mail.setParameterValue(MailActionExecuter.PARAM_FROM, getEmail(inviter));
        mail.setParameterValue(MailActionExecuter.PARAM_TO, getEmail(invitee));
        mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, buildSubject(properties));
        String mailText = buildMailText(properties, inviter, invitee);
        mail.setParameterValue(MailActionExecuter.PARAM_TEXT, mailText);
        actionService.executeAction(mail, getWorkflowPackage(properties));
    }

    /**
     * @param properties
     */
    private void checkProperties(Map<String, String> properties)
    {
        Set<String> keys = properties.keySet();
        if (!keys.containsAll(expectedProperties))
        {
            LinkedList<String> missingProperties = new LinkedList<String>(expectedProperties);
            missingProperties.removeAll(keys);
            throw new InvitationException("The following mandatory properties are missing:\n" + missingProperties);
        }
    }

    private String buildSubject(Map<String, String> properties)
    {
     return messageService.getMessage("invitation.invitesender.email.subject", getSiteName(properties));
    }

    private String buildMailText(Map<String, String> properties, NodeRef inviter, NodeRef invitee)
    {
        String template = getEmailTemplate();
        Map<String, Object> model = makeDefaultModel();
        Map<String, String> args = buildArgs(properties, inviter, invitee);
        model.put("args", args);
        return templateService.processTemplate(template, model);
    }

    private String getEmailTemplate()
    {
        NodeRef template = getEmailTemplateNodeRef();
        return template.toString();
    }

    private Map<String, String> buildArgs(Map<String, String> properties, NodeRef inviter, NodeRef invitee)
    {
        String params = buildUrlParamString(properties);
        String serverPath = properties.get(wfVarServerPath);
        String acceptLink = serverPath + properties.get(wfVarAcceptUrl) + params;
        String rejectLink = serverPath + properties.get(wfVarRejectUrl) + params;

        Map<String, String> args = new HashMap<String, String>();
        args.put("inviteePersonRef", invitee.toString());
        args.put("inviterPersonRef", inviter.toString());
        args.put("siteName", getSiteName(properties));
        args.put("inviteeSiteRole", getRoleName(properties));
        args.put("inviteeUserName", properties.get(wfVarInviteeUserName));
        args.put("inviteeGenPassword", properties.get(wfVarInviteeGenPassword));
        args.put("acceptLink", acceptLink);
        args.put("rejectLink", rejectLink);
        return args;
    }

    private String getRoleName(Map<String, String> properties) {
     String roleName = properties.get(wfVarRole);
     String role = messageService.getMessage("invitation.invitesender.email.role."+roleName);
     if(role == null)
     {
   role = roleName;
  }
     return role;
 }

 private Map<String, Object> makeDefaultModel()
    {
        NodeRef person = repository.getPerson();
        NodeRef companyHome = repository.getCompanyHome();
        NodeRef userHome = repository.getUserHome(person);
        Map<String, Object> model = templateService.buildDefaultModel(person, companyHome, userHome, null, null);
        return model;
    }

    private String getEmail(NodeRef person)
    {
        return (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL);
    }

    private NodeRef getWorkflowPackage(Map<String, String> properties)
    {
        String packageRef = properties.get(WF_PACKAGE);
        return new NodeRef(packageRef);
    }

    private NodeRef getEmailTemplateNodeRef()
    {
        StoreRef spacesStore = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
        String query = " PATH:\"app:company_home/app:dictionary/app:email_templates/cm:invite/cm:invite-email.ftl\"";

        SearchParameters searchParams = new SearchParameters();
        searchParams.addStore(spacesStore);
        searchParams.setLanguage(SearchService.LANGUAGE_LUCENE);
        searchParams.setQuery(query);

        ResultSet results = null;
        try
        {
            results = searchService.query(searchParams);
            List<NodeRef> nodeRefs = results.getNodeRefs();
            if (nodeRefs.size() == 1)
                return nodeRefs.get(0);
            else
                throw new InvitationException("Cannot find the email templatte!");
        }
        catch (SearcherException e)
        {
            throw new InvitationException("Cannot find the email templatte!", e);
        }
        finally
        {
            if (results != null)
                results.close();
        }
    }

    private String buildUrlParamString(Map<String, String> properties)
    {
        StringBuilder params = new StringBuilder("?inviteId=");
        params.append(properties.get(WF_INSTANCE_ID));
        params.append("&inviteeUserName=");
        params.append(URLEncoder.encode(properties.get(wfVarInviteeUserName)));
        params.append("&siteShortName=");
        params.append(properties.get(wfVarResourceName));
        params.append("&inviteTicket=");
        params.append(properties.get(wfVarInviteTicket));
        return params.toString();
    }

    private String getSiteName(Map<String, String> properties)
    {
        String siteFullName = properties.get(wfVarResourceName);
        SiteInfo site = siteService.getSite(siteFullName);
        if (site == null)
            throw new InvitationException("The site " + siteFullName + " could not be found.");

        String siteName = site.getShortName();
        String siteTitle = site.getTitle();
        if (siteTitle != null && siteTitle.length() > 0)
        {
            siteName = siteTitle;
        }
        return siteName;
    }
}gistry, null));
      
      // current date/time is useful to have and isn't supplied by FreeMarker by default
      model.put("date", new Date());
      
      // add custom method objects
      model.put("hasAspect", new HasAspectMethod());
      model.put("message", new I18NMessageMethod());
      model.put("dateCompare", new DateCompareMethod());
      model.put("url", new URLHelper(repoRemoteUrl));
      
      return model;
   }
    
    /**
     * Add the parameter definitions
     */
    @Override
    protected void addParameterDefinitions(List<ParameterDefinition> paramList) 
    {
        paramList.add(new ParameterDefinitionImpl(PARAM_TO, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TO)));
        paramList.add(new ParameterDefinitionImpl(PARAM_TO_MANY, DataTypeDefinition.ANY, false, getParamDisplayLabel(PARAM_TO_MANY), true));
        paramList.add(new ParameterDefinitionImpl(PARAM_SUBJECT, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_SUBJECT)));
        paramList.add(new ParameterDefinitionImpl(PARAM_TEXT, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TEXT)));
        paramList.add(new ParameterDefinitionImpl(PARAM_FROM, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_FROM)));
        paramList.add(new ParameterDefinitionImpl(PARAM_TEMPLATE, DataTypeDefinition.NODE_REF, false, getParamDisplayLabel(PARAM_TEMPLATE), false, "ac-email-templates"));
    }

    public void setTestMode(boolean testMode)
    {
        this.testMode = testMode;
    }

    public boolean isTestMode()
    {
        return testMode;
    }

    /**
     * Returns the most recent message that wasn't sent
     *  because TestMode had been enabled.
     */
    public MimeMessage retrieveLastTestMessage()
    {
        return lastTestMessage; 
    }

    public static class URLHelper
    {
        String contextPath;
        String serverPath;
        
        public URLHelper(String repoRemoteUrl)
        {
            String[] parts = repoRemoteUrl.split("/");
            this.contextPath = "/" + parts[parts.length - 1];
            this.serverPath = parts[0] + "//" + parts[2];
        }
        
        public String getContext()
        {
           return this.contextPath;
        }

        public String getServerPath()
        {
           return this.serverPath;
        }
    }
}

Below is context file for the same.Put it inside tomcat/shared/classes/alfresco/extension/mail-context.xml.



 
  
   
  
  
   
  
  
   
  
  
   
  
  
   
  
  
   
  
  
   
  
  
   ${mail.header}
  
  
   ${mail.from.default}
  
  
   ${repo.remote.url}
  

  
   ${mail.testmessage.to}
  
  
   ${mail.testmessage.subject}
  
  
   ${mail.testmessage.text}
  
 


Step 2 : Create Code for Executing Custom Action
We can execute Custom action from anywhere in alfresco ,whether it is workflow , javascript , webscript or inside any other custom action.We will use javascript for executing this code.

var mail = actions.create("mail");  
mail.parameters.to = "krutik.jayswal@gmail.com";  
mail.parameters.subject = "Html test";
mail.parameters.html = "true";  //For setting content as html.
mail.parameters.from = "alfresco.architect@mail.com";  
mail.parameters.text = "My name is Krutik Jayswal if you see this it is very bold";  
mail.parameters.node=document;  
mail.execute(document); 
I hope you have noticed line number 4 in above code.That is used for setting html content. Hope this helps.

Share this: