Mail merge without the mail.

A while back I stumbled across a situation where I needed to do a visualforce mail merge, but I didn’t want to send the email. Unfortunately, there’s no built in way to do that. Salesforce’s Visualforce merge code doesn’t give you a “getter” for the merge result. Instead, the normal workflow looks like this.

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[]{'theDoctor@who.com'};
    mail.setToAddresses(toAddresses);
    mail.setUseSignature(this.useSig);
    mail.setSaveAsActivity(this.saveActivity);
    mail.setSenderDisplayName(this.senderDisplayName);
    mail.setTargetObjectId(targetObjectId);
    mail.setTemplateId(templateId);
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});

In fact, there’s not even a .merge() method exposed in Apex. The merging happens as part of Messaging.sendEmail();

However, after some research I discovered that PJC over on Stackexchange had figured out that a DB savepoint could be (ab)used to grab the template contents after merging. This is Neat(c).

Fast forward a few months, (LifeWithRyan)[http://www.sudovi.com/] and I are talking on IRC about this same problem. We agreed to both blog our solutions. His is here: (When an Email Template just isn’t enough)[http://www.sudovi.com/when-an-email-template-just-isnt-enough/] I decided to wrap the method I found up in a reusable class: MailUtils.cls. Mailutils offers a single static method. getMergedTemplateForObjectWithoutSending(Id targetObjectId, Id templateId, Boolean useSig, Boolean saveActivity, String senderDisplayName) That takes the work out of this. It returns a Map, with the following keys:
textBody: Merged text body
htmlBody: Merged html version
subject: Subject line of the email

Here’s MailUtils.cls in its full ‘glory’:

public class mailUtils {
  public class mailUtilsException extends exception {}

  public Boolean useSig {get; private set;}
  public Boolean saveActivity {get; private set;}
  public String senderDisplayName {get; private set;}

  public mailUtils(Boolean useSig, Boolean saveActivity, String senderDisplayName){
    this.useSig = usesig;
    this.saveActivity = saveActivity;
    this.senderDisplayName = senderDisplayName;
  }

  // Derived from: 
  // http://salesforce.stackexchange.com/questions/13/using-apex-to-assemble-html-letterhead-emails/8745#8745
  public Messaging.SingleEmailMessage MergeTemplateWithoutSending(Id targetObjectId, Id templateId) {
    Messaging.reserveSingleEmailCapacity(1);
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    // Intentionally set a bogus email address.
    String[] toAddresses = new String[]{'invalid@emailaddr.es'};
    mail.setToAddresses(toAddresses);
    mail.setUseSignature(this.useSig);
    mail.setSaveAsActivity(this.saveActivity);
    mail.setSenderDisplayName(this.senderDisplayName);
    mail.setTargetObjectId(targetObjectId);
    mail.setTemplateId(templateId);

    // create a save point
    Savepoint sp = Database.setSavepoint();
    // Force the merge of the template.
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
    // Force a rollback, and cancel mail send.
    Database.rollback(sp);

    // Return the mail object
    // You can access the merged template, subject, etc. via:
    // String mailTextBody = mail.getPlainTextBody();
    // String mailHtmlBody = mail.getHTMLBody();
    // String mailSubject = mail.getSubject();
    return mail;

  }

  public static Map<String,String> getMergedTemplateForObjectWithoutSending(Id targetObjectId, Id templateId, Boolean useSig, Boolean saveActivity, String senderDisplayName) {
    Map<String,String> returnValue = new Map<String,String>();
    mailUtils mu = new mailUtils(useSig, saveActivity, senderDisplayName);
    Messaging.SingleEmailMessage mail = mu.MergeTemplateWithoutSending(targetObjectId, templateId);
    returnValue.put('textBody', mail.getPlainTextBody());
    returnValue.put('htmlBody', mail.getHTMLBody());
    returnValue.put('subject', mail.getSubject());
    return returnValue;
  }

}

Leave a Reply

Your email address will not be published. Required fields are marked *