+ Start a Discussion
Eric_SantiagoEric_Santiago 

Rendering merge fields in a String

Is there a way to render the merge fields in a string in Apex. As an example, I want to use email templates for a task. I've
written a VF page to override new task creation. It has a function that retrieves the body of the template but when its displayed
in the inputField it show the merge field syntax; i.e. Dear {!Contact.FirstName}.

 

public PageReference changeTemplate() {
EmailTemplate template = [Select TemplateType, Subject,
Name, Id, HtmlValue, Body, Description From EmailTemplate where id =:ApexPages.currentPage().getParameters().get('templateId')];

msg.Subject__c = template.Subject;
msg.Description__c = template.Body; //includes {!merge fields} as literal
}
return null ;
}

 

 I'm not sending an email as part of this process, so I need some way to render the merge fields in the string. What complicates this is that while the whoId can only be a Lead or Contact, the whatId on the task can any object. So a simple find and replace on the string gets very complex. Any suggestions?
ThomasTTThomasTT

Well, I don't think there is such a function... because, there is no way for SFDC to instanciate the object for the merge fields for yoru VF page or the controller.

 

It means, you can do that by your self. I usually define my message in a static string variable or Custom Label with my own placement folder {0} {1}... and when I use it for my error message or something, I do

 

string message = 'This is {0}';

string value = 'a pen';

message = message.replaceAll('\\{0\\}', value);

 

I think you have the objects to merge, so you can do that by your self. If you want to make it re-usable, that's gotta be a problem, but even that could be possible with dynamic access to SObjects (... or not).

 

ThomasTT

Eric_SantiagoEric_Santiago

Well, I ended up using dynamic sql to write a custom renderer. So now when a user selects an email template using my VF page, merge fields in the body of the template are replaced with the values of the objects related to the task. Here's the relevent function.

 

 

public String renderMergeFields(String emailBody) { //get the related objects to this task List<String> objects = new List<String>(); Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); Map<String,String> keyPrefixMap = new Map<String,String>{}; Set<String> keyPrefixSet = gd.keySet(); Map<String, String> fieldsValues = new Map<String,String>{}; //fieldname, field value for(String sObj : keyPrefixSet){ Schema.DescribeSObjectResult r = gd.get(sObj).getDescribe(); String tempName = r.getName(); String tempPrefix = r.getKeyPrefix(); System.debug('Processing Object['+tempName + '] with Prefix ['+ tempPrefix+']'); keyPrefixMap.put(tempPrefix,tempName); } String tPrefix; if(tsk.whatId!=null){ tPrefix = String.Valueof(tsk.whatId).subString(0,3); objects.add(keyPrefixMap.get(tPrefix)); System.debug('Task whatId Object Type: ' + keyPrefixMap.get(tPrefix)); } if(tsk.whoId!=null){ tPrefix = String.Valueof(tsk.whoId).subString(0,3); objects.add(keyPrefixMap.get(tPrefix)); System.debug('Task whoId Object Type: ' + keyPrefixMap.get(tPrefix)); } //special merge fields; object owner fields Map<Id, User> owners = new Map<id, User>([Select Id, FirstName, LastName, Title, Phone, UserType, IsActive from User Where IsActive = True and (UserType = 'Standard' OR UserType = 'Partner')]); //special merge field; Sending user objects.add('User'); for (String objName : objects){ Schema.SObjectType obj = gd.get(objName) ; Map<String, Schema.SObjectField> M = obj.getDescribe().fields.getMap() ; String objFields = ''; for (Schema.SObjectField field : M.values()){ String fieldname = field.getDescribe().getName(); objFields += fieldname + ','; } objFields = objFields.substring(0, objFields.lastIndexOf(',')); //trim last comma; System.debug('objFields: ' + objFields); Sobject relObj; String qryString = 'SELECT ' + objFields + ' FROM ' + objName; if (objName != 'User'){ relObj = Database.query(qryString + ' limit 1'); } else { qryString = qryString + ' WHERE ' + objName + '.Id =' + '\'' + UserInfo.getUserId() + '\'' ; System.debug('qryString: ' + qryString); relObj = Database.query(qryString) ; } for(String s : M.keySet()) { try { System.debug('{!' + objName + '.' + m.get(s).getDescribe().getName() + '} = ' + relObj.get(s)); if (relObj.get(s) != null){ fieldsValues.put('{!' + objName + '.' + m.get(s).getDescribe().getName() + '}', String.valueOf(relObj.get(s)) ); } else { fieldsValues.put('{!' + objName + '.' + m.get(s).getDescribe().getName() + '}', ''); } } catch(System.Exception e) { //just ignore. field is not available in query. } } //special merge fields; object owner fields if (M.containsKey('ownerid')){ String ownerId = String.valueOf(relObj.get('ownerid')); if (owners.get(ownerId).FirstName != null){ fieldsValues.put('{!' + objName + '.' +'OwnerFirstName'+ '}', owners.get(ownerId).FirstName); } else { fieldsValues.put('{!' + objName + '.' +'OwnerFirstName'+ '}', ''); } if (owners.get(ownerId).LastName != null){ fieldsValues.put('{!' + objName + '.' +'OwnerLastName'+ '}', owners.get(ownerId).LastName); } else { fieldsValues.put('{!' + objName + '.' +'OwnerLastName'+ '}', ''); } if (owners.get(ownerId).Title != null){ fieldsValues.put('{!' + objName + '.' +'OwnerTitle'+ '}', owners.get(ownerId).Title); } else { fieldsValues.put('{!' + objName + '.' +'OwnerTitle'+ '}', ''); } if (owners.get(ownerId).Phone != null){ fieldsValues.put('{!' + objName + '.' +'OwnerPhone'+ '}', owners.get(ownerId).Phone); } else { fieldsValues.put('{!' + objName + '.' +'OwnerPhone'+ '}', ''); } } //need to handle special fields in Case merge templates System.debug('here objName: ' + objName); if (objName == 'Case'){ try { Map<Id, CaseSolution> cSols = new Map<Id, CaseSolution>([Select SolutionId, IsDeleted, Id, CaseId From CaseSolution Where IsDeleted = False AND CaseId = :tsk.WhatId ]); List<ID> cSolIds = new List<Id>(); For (Id cSol : cSols.keyset()){ System.debug('cSols.get(cSol).SolutionId: ' + cSols.get(cSol).SolutionId); cSolIds.add(cSols.get(cSol).SolutionId); } System.debug('Case Solutions found: ' + cSols.size()); List<Solution> sols = new List<Solution>([Select SolutionNote, SolutionName, Id, (Select Id From Attachments) From Solution Where Id in :cSolIds]); System.debug('Solutions found: ' + Sols.size()); if (sols.size() > 0){ String Subject = ''; String Description = ''; String SubjectDescription = ''; Integer count = 1; for (Solution sol : sols){ Subject += sol.SolutionName + '\r\n\r\n'; Description += sol.SolutionNote + '\r\n\r\n'; SubjectDescription += '----------------------------------------------------------------------------' + '\r\n' + '(' +count + ') "' + sol.SolutionName + '"' + '\r\n' + '----------------------------------------------------------------------------' + '\r\n\r\n' + sol.SolutionNote + '\r\n\r\n'; count++ ; } System.debug('Solutions subject: ' + Subject); fieldsValues.put('{!Case.Solution_Subject}', Subject); System.debug('Solutions description: ' + Description); fieldsValues.put('{!Case.Solution_Description}', Description); fieldsValues.put('{!Case.Solution_Subject_and_Description}', SubjectDescription); fieldsValues.put('{!Case.Suggested_Solutions}', ''); fieldsValues.put('{!Case.Solution_Attachments}', ''); } } catch (Exception e) { //default to null string, in case there are no attached solutions fieldsValues.put('{!Case.Solution_Subject}', ''); fieldsValues.put('{!Case.Solution_Description}', ''); fieldsValues.put('{!Case.Solution_Subject_and_Description}', ''); fieldsValues.put('{!Case.Suggested_Solutions}', ''); fieldsValues.put('{!Case.Solution_Attachments}', ''); } } } for(String s : fieldsValues.keySet()) { System.debug('Replacing' + s + ' with ' + fieldsValues.get(s)); emailBody = emailBody.replace(s, fieldsValues.get(s)); } return emailBody; }

 

 

 

ThomasTTThomasTT
Wow, impressive!
sureshcsksureshcsk

In the Email template I added {!Contact.name},the email is sent throught Apex coding.
We need to merege {!Contact.name} before the email is sent.

Logic:
1.check whether the template contains the {!Contact.name} .
2.If so replace the {!Contact.name}  with Contact.Name which is got by quering.
3.Then pass the replaced template to mail body.

so in Apex I just made a simple code as follows
public class Mail_Contorller
{
String contact_fullname;
Boolean contact_fullname_result;

contact_fullname='{!Contact.Name}';

/**checking whether {!Contact.name} is present in the template**/
contact_fullname_result=template_body.contains(contact_fullname);

if(contact_fullname_result)
{
usercontact = [Select name From Contact Where email =: email_id];
template_body=template_body.replace(contact_fullname,usercontact.name);
}



}

 

Then pass the replaced template_body to Mail Message body.
Like this you can check '{!Contact.LastName}','{!Contact.FirstName}'...etc

cheers
suresh

James GriffinJames Griffin
Hi all, 

Has the state-of-affairs for this problem changed at all? I am trying to solve this myself and can't find a neat way of receiving the rendered version of an email template so I can further manipulate it. Has a better solution than producing a custom renderer appeared in recent times?

Thanks, 
Griff