The Force.com platform provides rich set of features to send emails through the apex code. It provides different templates like visualforce templates, HTML email templates and text templates so that the developers can create rich email experience. The beauty of these templates is that as a developer you can include the fields from an object, so that the emails will be completely customized for the given scenario. For e.g. if you want to send a nicely formatted HTML emails with the opportunity information to your sales group, then you can include the opportunity related fields such as the opportunity name, whether the opportunity is won or lost, etc. There are several articles that covers the basics of email services like this and this. By default, as a developer, you can only send the values of the object fields in the email and cannot include anything that is dynamically computed. This article will show you how to dynamically populate the content of a custom HTML email template with custom data. For e.g. adding dynamically calculated data such as number of days it took to won the opportunity, total number of sales by a sales person in a given month, etc. I’m also going to show you how you can write some generic code that can take varying dynamic parameters, populate the custom HTML email template with those dynamic parameters along with the standard/custom fields from standard/custom objects and send an email to the recipients. What is unique in this approach is that it is not only possible to populate the email body text with custom created dynamic data, but also possible to populate the email subject with custom created dynamic data.
For this demonstration, let us take the following use case. The requirement is to send an email notification on the Case object whenever a case is not closed and the case is still pending even after the set SLA period. For e.g. if a case is pending for 5 days since it was opened and if the SLA was set for 5 days, then on the 6th day, an email notification needs to be sent. Here is the screenshot of how the email will look alike when it is sent. The values highlighted in the red come from the standard fields, while the values highlighted in blue are calculated dynamically.
Here is the high level approach to accomplish this.
-
Create the custom HTML email template
-
Create the logic to populate the subject and the body text and send the email
-
Create a apex scheduler to send the email
1. Create the custom HTML email template
A. The following screenshots show the steps in creating the custom HTML email template.
B. In order to include the dynamically calculated data, follow the same convention of using the merge fields. For e.g to include the case status, include ‘{!Case.Status}. Please note that unlike in the standard merge field feature in the HTML templates, here you can use any format for the merge field syntax. For the custom data, I suggest to use the variable name that you used to calculate the dynamic data. In the following example, ‘{!numDays} indicate the number of days the case is open since it was created.
C. Specify the text for the plain text email. You can click the ‘Copy text from HTML version’ button to copy the content in the HTML template, unless you want to specify a different text for plain text email.
Click ‘Save’.
2. Create the logic to populate the subject and the body text and send the email
In the above example, the subject has two dynamic parameters and the body text has 6 dynamic parameters. Each requirement is different and we need a generic method to accommodate different number of parameters for both subject and the body text. To accomplish that, I have used two ‘maps’, one for subject and another for body text. This way, we can add any number of dynamic parameters and able to customize based on the requirements. I have created two apex classes to accommodate this logic:
-
EmailMessageWrapper – Wraps email message. Provides different constructors to capture email information.
-
UtilityClass – Provides method(s) to send emails. One of the method supports sending email using email templates and this is the method that we will use for this demo.
The following code snippet from EmailMessageWrapper shows the constructor that we will use in this demo.
1: public EmailMessageWrapper(String fromAddr, Id toAddrId, String sub, Map<String, String> mapSubjectParams, Map<String, String> mapBodyParams) {
2: this(fromAddr, null, toAddrId, null, sub, mapSubjectParams, null, mapBodyParams );
3: }
4:
5: public EmailMessageWrapper(String fromAddr, String toAddr, Id toAddrId, String bccAddr, String sub, Map<String, String> mapSubjectParams, String body, Map<String, String> mapBodyParams) {
6: this.FromAddress = fromAddr;
7: this.ToAddress = toAddr;
8: this.ToAddressId = toAddrId;
9: this.BccAddress = bccAddr;
10: this.Subject = sub;
11: this.ParameterSubjectMap = mapSubjectParams;
12: this.Body = body;
13: this.ParameterBodyMap = mapBodyParams;
14: }
The mapSubjectParams and the mapBodyParams parameters holds the dynamic data as a key/value pair. The key corresponds to the merge field that is defined in the custom HTML email template, while the ‘value’ is the data that will be merged into the merge field.
The following code snippet from UtilityClass shows the sendEmail method that takes two parameters. The first parameter is a list of EmailMessageWrapper and the second parameter is the email template name.
1: public static void sendEmail(List<EmailMessageWrapper> listEmailMessageWrapper, String emailTemplateName) {
2: List<Messaging.SendEmailResult> listEmailResult = null;
3: List<Messaging.Singleemailmessage> listSingleEmailMessages = new List<Messaging.Singleemailmessage>();
4: EmailTemplate emailTemplate = [SELECT Id, Subject, HtmlValue, Body FROM EmailTemplate WHERE Name = :emailTemplateName];
5: for (EmailMessageWrapper emailMessageWrapper : listEmailMessageWrapper) {
6: Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
7: mail.setSenderDisplayName('Case Age Notification');
8: if(emailMessageWrapper.FromAddress != null && emailMessageWrapper.FromAddress.length() > 0)
9: mail.setReplyTo(emailMessageWrapper.FromAddress);
10: if(emailMessageWrapper.ToAddress != null && emailMessageWrapper.ToAddress.length() > 0)
11: mail.setToAddresses(new String[] { emailMessageWrapper.ToAddress });
12: else
13: mail.setTargetObjectId(emailMessageWrapper.ToAddressId);
14: if(emailMessageWrapper.BccAddress != null && emailMessageWrapper.BccAddress.length() > 0)
15: mail.setBccAddresses(new String[] {emailMessageWrapper.BccAddress });
16: String subject = null;
17: if(emailMessageWrapper.Subject != null && emailMessageWrapper.Subject.length() > 0) {
18: mail.setSubject(emailMessageWrapper.Subject);
19: subject = emailMessageWrapper.Subject;
20: }
21: else
22: subject = emailTemplate.Subject;
23:
24: for(String key: emailMessageWrapper.ParameterSubjectMap.keySet())
25: subject = subject.replace(key, (emailMessageWrapper.ParameterSubjectMap.get(key) == null ? '' : emailMessageWrapper.ParameterSubjectMap.get(key)));
26:
27: mail.setSubject(subject);
28: String htmlBody = emailTemplate.HtmlValue;
29: String plainBody = emailTemplate.Body;
30: for (String key : emailMessageWrapper.ParameterBodyMap.keySet()) {
31: htmlBody = htmlBody.replace(key, (emailMessageWrapper.ParameterBodyMap.get(key) == null) ? '' : emailMessageWrapper.ParameterBodyMap.get(key));
32: plainBody = plainBody.replace(key, (emailMessageWrapper.ParameterBodyMap.get(key) == null) ? '' : emailMessageWrapper.ParameterBodyMap.get(key));
33: }
34:
35: mail.setHtmlBody(htmlBody);
36: mail.setSaveAsActivity(false);
37: mail.setPlainTextBody(plainBody);
38: listSingleEmailMessages.add(mail);
39: }
40: if(!Test.isRunningTest())
41: listEmailResult = Messaging.sendEmail(listSingleEmailMessages);
42: }
What you see in the above code is that we iterate through the maps to populate the dynamic data in the HTML email template content by replacing the merge field with the dynamic data from the map variable. Since it is a map, we can add any number of dynamic parameters as key/value pairs, where ‘key’ represents the merge field and the ‘value’ represents the data for the merge field.
3. Create a apex scheduler to send the email
The final piece of this demo is to create the apex scheduler to send the email. The following code snippet is from the CaseAgeNotificationExecute class which retrieves the eligible cases and sends an email notification.
1: public static void processSchedules() {
2: Integer slaTime = 3;
3: List<EmailMessageWrapper> listEmailMessageWrapper = new List<EmailMessageWrapper>();
4:
5: String slaTimeLabel = 'Days';
6: List<Case> listCases = [SELECT Id, CaseNumber, CreatedDate, OwnerId, Status FROM Case WHERE Status <> 'Closed'];
7: for(Case c : listCases) {
8: if(DateTime.now() > c.CreatedDate.addDays(slaTime)) {
9: long numDays = (DateTime.now().getTime() / 1000 / 60 / 1440) - (c.CreatedDate.getTime() / 1000 / 60 / 1440);
10: Map<String, String> mapSubjectParams = new Map<String, String> {
11: '{!Case.Status}' => c.Status,
12: '{!numDays}' => String.valueOf(numDays)
13: };
14:
15: Map<String, String> mapBodyParams = new Map<String, String> {
16: '{!Case.CaseNumber}' => c.CaseNumber,
17: '{!Case.Status}' => c.Status,
18: '{!numDays}' => String.valueOf(numDays),
19: '{!Case.CreatedDate}' => c.CreatedDate.date().format(),
20: '{!slaTime}' => String.valueOf(slaTime),
21: '{!slaTimeLabel}' => slaTimeLabel
22: };
23: listEmailMessageWrapper.add(new EmailMessageWrapper('admin@salesforce.com', c.OwnerId, null, mapSubjectParams, mapBodyParams));
24: }
25: }
26: if(listEmailMessageWrapper.size() > 0)
27: UtilityClass.sendEmail(listEmailMessageWrapper, 'Case Aging Notification Email Template');
28: }
To test this, you can call the CaseAgeNotificationExecute.processSchedules() method from either the developer console or using anonymous block from your eclipse IDE.
The code for entire set of classes referred in this article can be downloaded from here.



