When an Email Template Just Isn’t Enough

Sometimes your users need to send an email from within Salesforce. Most of the time the standard Send Email button will suffice. Other times however, your needs are more specific. For those times, developers often resort to using Visualforce email templates which can certainly meet just about any need. However, the drawback here is that the email content now lives in code. Being good developers, we don’t want our admins to have to worry about digging into code for things like this — unless they really want to — so we try to come up with a way to make the content of the email accessible. This was my exact requirement and here’s what I wound up doing. The code herein is very specific to the requirement in question but could be generalized a bit to make it more flexible or reusable (which is something I may just do at some point, but alas — deadlines. @Codefriar has already done some pretty cool stuff here.).

Here was my approach:

The Template:

I created a new Email Template just as one would for use in workflow email alerts etc. I then created a custom settings to store the ID of this email template in this particular application’s custom settings. I will use this ID later to get the ID of this template in my apex class.  In the content of this email I included the merge field syntax (more or less because it was familiar to people) for the fields that I knew I was going to be including later.  In the end I wound up with a template that looked something like this (any client identifiable content has been replaced with BLAH BLAH BLAH — I’m so creative…):

Dear {!Contact.Salutation} {!Contact.LastName},
I am pleased to attach the final BLAH BLAH BLAH document between {!Opportunity.Account} and the BLAH BLAH BLAH for your records.

BLAH BLAH BLAH is proud to work with {!businessTypePlural} such as yours to fulfill new opportunities in BLAH BLAH BLAH.

Thank you for your commitment to your employees, your community and the state of BLAH BLAH BLAH.

BLAH BLAH BLAH is always looking to improve our services. Please take the time to fill out a brief satisfaction survey using this link:

{!surveyLink}

Sincerely,
Some Person at BLAH BLAH BLAH

Some of those items I could have set using the standard setTargetObjectId, setWhatId methods available on Messaging.SingleEmailMessage, but those wouldn’t get me to some of the other data that I needed. So instead, I used the template merely for its body content and manually performed string replacements in my apex class on that body like so:

public EmailTemplate emailTemplate {
        get {
            if(emailTemplate == null) {
                Survey_Settings__c settings = Survey_Settings__c.getInstance();
                if(settings != null) {
                    emailTemplate = [SELECT Id
                                     , Subject
                                     , Body
                                     , HtmlValue 
                                     FROM EmailTemplate 
                                     WHERE ID = :settings.Contract_Email_Template_ID__c];     
                }
            }

            //send in the Contact for salutation purposes
            if(this.selectedContact != null) {
                Contact c = this.oppContacts.get(selectedContact);
                if(c != null) {
                    if(c.Salutation != null) {
                        emailTemplate.Body = emailTemplate.Body.replace('{!Contact.Salutation}', c.Salutation);    
                    } else if(c.FirstName != null) {
                        emailTemplate.Body = emailTemplate.Body.replace('{!Contact.Salutation}', c.FirstName);    
                    }
                    
                    emailTemplate.Body = emailTemplate.Body.replace('{!Contact.LastName}', c.LastName);
                }
            }

            emailTemplate.Body = emailTemplate.Body.replace('{!Opportunity.Account}', this.opp.Account.Name);
            emailTemplate.Body = emailTemplate.Body.replace('{!businessTypePlural}', this.opp.Account.Business_Type_Plural__c);
            
            return emailTemplate;    
        }
        set;
        
    }

Much of the above, (if not all) could have been achieved by just sending in my contact. I could have gotten most of the necessary information from there, however — in so doing, the body of the email would have been rendered but I still don’t have all of my information, particularly my survey link. I wouldn’t have my survey ID yet either as that doesn’t get created until my end user clicks send. By then it would be too late for me to stick the survey link since the email message would be already packaged up. (I did try this, before going this route but ran into some odd issues getting my link in the email message, so I backed out and moved forward with this plan). Add to that the fact that I wanted to show the content of the email WITH the substitutions in place to the end user for review.

Lastly, once the user has selected their attachment and has proofread, perhaps even modified the message (minus the link since it doesn’t exist  yet) they click send. I then save all the necessary objects, including my survey and stuff that into the email message before calling my sendEmail method.

private Messaging.SendEmailResult[] sendEmail() {
        Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
        List attachments = new List();
        Messaging.EmailFileAttachment contractFile = new Messaging.EmailFileAttachment();
        contractFile.setFileName(emailFile.name);
        contractFile.setBody(emailFile.body);
        attachments.add(contractFile);
        msg.setFileAttachments(attachments);
        msg.setSubject(emailTemplate.Subject);
        msg.setToAddresses(new String[] { this.oppContacts.get(selectedContact).Email});
        msg.setHtmlBody(emailTemplate.Body);
        Messaging.SendEmailResult[] result = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {msg});
        return result;
    }

Which gets called from this hunk of code in my save method on the controller.

Survey_Settings__c settings = Survey_Settings__c.getInstance();
                if(settings != null && settings.Customer_Satisfaction_Survey_URL__c != null) {
                    String surveyUrl = settings.Customer_Satisfaction_Survey_URL__c + '?aid=' + this.survey.ID;
                    emailTemplate.Body = emailTemplate.Body.replace('{!surveyLink}', surveyUrl);    
                }   

                //send the email
                emailTemplate.Body = emailTemplate.Body.replace('\n', '
');
                Messaging.SendEmailResult[] result = sendEmail();

I then manually create an activity that logs this message and includes the body of the email in the description field of the task. The task is set to Completed in code so it goes directly to Activity History.

In the end, the solution provides full access to content to the admin, and full control over the flow complete with some extra automation that they couldn’t get out of their old process.

Hope this helps should ever find yourself in a similar situation…

 

:wq!