Custom values for tokens in Sitecore 9 EXM

Use tokens in EXM and fill it with custom personalized values instead of just using the values of contact facets.

Grid with mail and person icons.
Grid with mail and person icons.

My solution is based on the "custom personalization token" from Sitecore docs.

We use custom tokens in EXM, which we filled with values from a Sitecore item instead of the values from contact facets. You can also choose any other source to fill your tokens with this solution. For example, we had a custom token $consultantName$ and we had the custom facet consultantNr and consultantName. In the facet consultantNr we had a personalnummer, which is also in a field in a Sitecore item. My plan was to take the consultantNr from the contact facet during the sending process of an exm newsletter. With this consultantNr I could find the matching consultant item from which I got the information for my token (consultantName). This information has to be filled into the token for each recipient, before the mail sent to them.

My solution was to do this logic in the custom DispatchTask. First, I created a customFacet for consultandNr and consultantName and a matching custom token (Sitecore docs). I filled the consultandNr into the facet of the contacts with the listmanager (see Sitecore docs). Usually, I would now replace the tokens with the value of the facets. This is exactly what I will do, but first I will change the value of the facets before I replace the tokens with the value of the facets. For this, I added my logic to the custom DispatchTask in the GetContact override.

Usually, the code for a custom DispatchTask would look like this:

public class CustomValueDispatchTask : DispatchTask
{
    private IContactService _contactService;
    public CustomValueDispatchTask([NotNull] ShortRunningTaskPool taskPool, [NotNull] IRecipientValidator recipientValidator, [NotNull] IContactService contactService, [NotNull] EcmDataProvider dataProvider, [NotNull] ItemUtilExt itemUtil, [NotNull] IEventDataService eventDataService, [NotNull] IDispatchManager dispatchManager), [NotNull] EmailAddressHistoryManager emailAddressHistoryManager, [NotNull] IRecipientManagerFactory recipientManagerFactory, [NotNull] SentMessageManager sentMessageManager) 
	: base(taskPool, recipientValidator, contactService, dataProvider, itemUtil, eventDataService, dispatchManager)emailAddressHistoryManager, recipientManagerFactory, sentMessageManager)
    {
        _contactService = contactService;
    }
    protected override IReadOnlyCollection<IEntityLookupResult<Contact>> GetContacts(List<DispatchQueueItem> dispatchQueueItems)
    {
        return _contactService.GetContacts(dispatchQueueItems.Select(x => x.ContactIdentifier), PersonalInformation.DefaultFacetKey, EmailAddressList.DefaultFacetKey, ConsentInformation.DefaultFacetKey, PhoneNumberList.DefaultFacetKey, ListSubscriptions.DefaultFacetKey, EmailAddressHistory.DefaultFacetKey, MyCustomFacet.DefaultFacetKey);
    }
}

But I added my logic as followed:

public class CustomValuesDispatchTask : DispatchTask
{
	private IContactService _contactService;
	public CustomValuesDispatchTask(ShortRunningTaskPool taskPool, IRecipientValidator recipientValidator, IContactService contactService, EcmDataProvider dataProvider, ItemUtilExt itemUtil, IEventDataService eventDataService, IDispatchManager dispatchManager, EmailAddressHistoryManager emailAddressHistoryManager, IRecipientManagerFactory recipientManagerFactory, SentMessageManager sentMessageManager)
	: base(taskPool, recipientValidator, contactService, dataProvider, itemUtil, eventDataService, dispatchManager, emailAddressHistoryManager, recipientManagerFactory, sentMessageManager)
	{
		_contactService = contactService;
	}
	protected override IReadOnlyCollection<IEntityLookupResult<Sitecore.XConnect.Contact>> GetContacts(List<DispatchQueueItem> dispatchQueueItems)
	{
		var contacts = _contactService.GetContacts(dispatchQueueItems.Select(x => x.ContactIdentifier), PersonalInformation.DefaultFacetKey, EmailAddressList.DefaultFacetKey, ConsentInformation.DefaultFacetKey, PhoneNumberList.DefaultFacetKey, ListSubscriptions.DefaultFacetKey, EmailAddressHistory.DefaultFacetKey, MyCustomFacet.DefaultFacetKey);
		var contactList = new List<IEntityLookupResult<Sitecore.XConnect.Contact>>();
		using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
		{
			foreach (var contact in contacts)
			{

				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
				//Contact Handler
				//Now you can update the Facets of the Contact with any Information from any source you want.
				//For example i get a Sitecore Item with the matching ConsultantNr and write the information of this item into the Facets of the contact.
				//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
				var tempContact = contact.Entity.GetFacet<MyCustomFacetModel>(MyCustomFacet.DefaultFacetKey); 
				//How to create a Custom facet class (MyCustomFacetModel) => https://doc.sitecore.com/developers/90/sitecore-experience-platform/en/create-a-custom-facet.html
				try
				{
					if (tempContact != null && !string.IsNullOrEmpty(tempContact.ConsultantNr))
					{
						//Get the Resources Folder where i saved al Consultant Items
						var consultantFolder = Sitecore.Context.Database.GetItem(new ID(Constants.Sitecore.Resources.ConsultantRootFolderID));
						//Get the matching consultant item
						var consultantItem = consultantFolder.Children.FirstOrDefault(x => x[MyConstants.ConsultantNrFieldId] == tempContact.ConsultantNr);
						
						if (consultantItem == null)
						{
							throw new Exception("No Matching Consultant found in Sitecore for ConsultantNr: " + tempContact.ConsultantNr);
						}
						
						//Take Lastname and Firstname from the Consultant Item and write it to the Facet (ConsultantName) of the Contact.
						var consultantName = new StringBuilder();
						consultantName.Append(consultantItem[MyConstants.LastNameFieldId] + " " + consultantItem[MyConstants.FirstNameFieldId]);
						tempContact.ConsultantName = consultantName.ToString();

					}
				}
				catch (Exception ex)
				{
					//Handle Exception
					//throw ex;
					//continue;
				}
				//Add the updated contact to the contactList
				contactList.Add(contact);
			}
			
		}
		return contactList;
	}
}

What I am doing is to fill up the facets with my values during the reading of the contact. I don’t write the value into the facet of the contact. I have the values just in the contact object which I will use to dispatch the mail. I don’t fill the token in the CustomRecipientPropertyTokenMap class, because there I have no access to the contact and I am more flexible in the custom DispatchTask.

This is the way how you can add values from any source to tokens which don’t have the value in the facets of the contact.

VIU AGRennweg 388001 ZürichCH-Switzerland+41 44 500 96 76
Imprint