Find your content:

Search form

You are here

Using APEX to assemble HTML Letterhead Emails

 
Share

I am looking to assemble an HTML Letterhead Email Template for an email service integration project.

There are two sObjects that hold this information - EmailTemplate and BrandTemplate.

BrandTemplate.value = broken HTML (styles aren't associated to tags) Letterhead with CDATA tags throughout to hold the email Template info

EmailTemplate.HTMLValue = User-created HTML email template

I've been trying to merge the two (don't need to worry about merge fields / codes) with little success.

I have tried turning both into XML, navigating the BrandTemplate.value field, but Salesforce does not natively support CDATA. I've also tried the polar opposite - stripping everything into text and doing replaces and substrings to assemble, but that also fails.

My end goal is to have a completed single HTML document consisting of both the BrandTemplate and EmailTemplate values.

Any hints / thoughts / clues?


Attribution to: Andy Boettcher

Possible Suggestion/Solution #1

You should be able to create a VF Email Template, and embed a VF Component within it to query those sObjects with an Apex controller.


Attribution to: Tom Gersic

Possible Suggestion/Solution #2

Summer 16 introduces two new methods that (finally) eliminate the clever hack by @pjcarly:

For VF, HTML, and Plain Text templates:

Messaging.SingleEmailMessage email = 
            Messaging.renderStoredEmailTemplate(templateId, whoId, whatId);

To get just the merged results from the templateBody (and any errors)

List<Messaging.RenderEmailTemplateBodyResult> resList = 
        Messaging.renderEmailTemplate(String whoId, String whatId, List<String> bodies)

where bodies is a list of strings corresponding to the value of EmailTemplate.body as queried from the database

for (EmailTemplate et: [select Body from EmailTemplate where DeveloperName IN ('foo','bar')])
  bodies.add(et.Body); 

Attribution to: cropredy

Possible Suggestion/Solution #3

Salesforce hacking time: I had this exact requirement, and was stuck for a while because of the weird connection between EmailTemplate and BrandTemplate, and the inability to leverage a merging/parsing function however, I found a solution, this is far from a clean solution, but it seems to be working perfectly.

First what you will do is, create a single email message (like you would send it from apex):

// Here we will build the single email message
Messaging.reserveSingleEmailCapacity(1);
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[]{'invalid@emailaddr.es'};
mail.setToAddresses(toAddresses);
mail.setUseSignature(false);
mail.setSaveAsActivity(false);
mail.setSenderDisplayName('MMPT');
mail.setTargetObjectId(UserInfo.getUserId());
mail.setTemplateId(sendTemplate.Id);

What we will do next is trick salesforce in sending the single email message, by setting a savepoint, sending the message and rolling back. The single email will not be send because of the rollback.

Savepoint sp = Database.setSavepoint();
Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
Database.rollback(sp);

But now the beauty comes, because we triggered the sendEmail method, the email template got merged with the brandtemplate and the parsing took place. And you can find the parsed versions of your template components in the next variables (Subject, Text and HTML)

String mailTextBody = mail.getPlainTextBody();
String mailHtmlBody = mail.getHTMLBody();
String mailSubject = mail.getSubject();

This is far from what salesforce meant to do with single email message, and since we cannot leverage the platform parsing and merging functions, this did gave me a solution to this problem unfortunately through some hacking.


Attribution to: pjcarly

Possible Suggestion/Solution #4

I had some other idea to do this.

EmailTemplate template = [
SELECT Id, Name, Subject, Body, HtmlValue
FROM EmailTemplate
WHERE Name = 'My Name'];
templateId = template.Id;

Custom_Object__c obj = [
SELECT Name, Custom_Field_1__c, Custom_Field_2__c
FROM Custom_Object__c
WHERE Id = :my.Id];

Map<String, String> data = new Map<String, String>();
for(String key:new List<String> {'Name', 'Custom_Field_1__c', 'Custom_Field_2__c' })
{
    data.put('{!Custom_Object__c.' + key + '}', (String) obj.get(key) );
    System.debug( LoggingLevel.ERROR,'@@@ key : ' + key );     
    System.debug( LoggingLevel.ERROR,'@@@ obj.get(key) : ' + obj.get(key) );   

}
data.put('User.FirstName',UserInfo.getFirstName());
data.put('User.LastName',UserInfo.getLastName());
String body = template.HtmlValue;
System.debug(LoggingLevel.ERROR,body);
System.debug(LoggingLevel.ERROR,'@@@  template.HtmlValue: ' +  template.HtmlValue );
System.debug( LoggingLevel.ERROR,'@@@   template.Subject: ' +   template.Subject );

String subj = template.Subject;
for(String key:data.keySet())
{
    body = body.replace(key, data.get(key));
    subj = subj.replace(key, data.get(key));
}

Unfortunately it does not for email templates with letter heads.


Attribution to: Patlatus

Possible Suggestion/Solution #5

When accessing templates and needing multiple objects I will
- select the template from the database
- define a merge syntax, use regular expressions "{!([A-z_]+)}" to get a key name - pass a map of data to replace the key (if exists) with data

you now have a template which is just a string. you can push the template to a VF page with or send an email manually in code.

code example:

public static Map<String,String> merge_template(Map<String,String> obj, EmailTemplate email){
    Map<String,String> data = new Map<String,String>();
    String html = email.HtmlValue;
    if(html!=null){
        html = m(html,obj,Pattern.compile('(\\{![A-Za-z_]+\\})').matcher(html));
        data.put('HtmlValue',html);
    }
    else{
        data.put('HtmlValue','');
    }
    String body = email.Body;
    String subject = email.Subject;
    body = m(body,obj,Pattern.compile('(\\{![A-Za-z_]+\\})').matcher(body));

    data.put('Body',body);

    return data;

}
private static String m(String body,Map<String,String> obj,Matcher m){
    while(m.find()){
      String merge_field = m.group(0);
      String field = merge_field.substring(1,merge_field.length()-1);
      String value;
      try{
        value = (String) obj.get(field);
      }
     catch(Exception e){
        value='';
     }
     if(value!=null){
        body = body.replace(merge_field,value);
     }
     else{
        body = body.replace(merge_field,'');
     }
    }
    return body;
}

public static Map<String,String> build(String template,Map<String,String> data){
    EmailTemplate[] tpls = [SELECT HtmlValue,Body,Subject FROM EmailTemplate WHERE DeveloperName = :template];
    if(tpls.isEmpty() == true){
        return "";
    }
    return merge_template(data,tpls.get(0);
}

Attribution to: ebt

Possible Suggestion/Solution #6

try to user the setTemplateId field of Mail.

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
EmailTemplate et=[SELECT id,name, subject,HtmlValue FROM EmailTemplate where name='xyz'];
mail.setTemplateId(et.id);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

No need to do any custom parsing.


Attribution to: Ysr Shk
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/13

My Block Status

My Block Content