Use these common use case examples to guide your custom personalization interaction setup for bulk sends.
Example 1: Custom Subject Line with Title
This Bulk Send Custom Personalization Interaction example allows a user to specify a custom subject to override the default subject. This example includes adding the first name, last name, and title for the contacts or leads to the custom subject. There’s an entry for the custom subject in your Marketing Cloud Engagement Data Extension and in your Profile Attributes. In this example, we use the customSubjectLine field.
In Marketing Cloud Engagement, edit the email and select a dynamic subject. Click the
lightning bolt below the subject. Specify a default subject and create a rule. The
subject for this new rule is %%customSubjectLine%%. Drag the customSubjectLine profile attribute
into the filters and change “is equal to” to “isn’t empty.” Save all the changes and
save the email.
In this example, the Apex class is BulkSendCustomTitleCPI.cls.
// This plugin assumes a column is created on the DM Entry Source DE with "customSubjectLine"
// It returns a Map<String, Map<String, String>> which is the approval.objectId mapped to the key / value pairs
// that belong to your CPI, and the keys should map to DE Entry Source DE columns if you add more.
global class BulkSendCustomTitleCPI implements mcdm_15.BulkSendPlugin {
global static Map<String, Map<String, String>> getBulkSendPluginData(List<mcdm_15.JourneyApproval> approvals, Map<String, String> pluginData) {
Map<String, Map<String, String>> IdsToCustomData = new Map<String, Map<String, String>>();
Map<String, String> processedCustomData = new Map<String, String>();
// Custom data passed in from front end component
String key = 'customSubjectLine';
// Here we get the title map from the contact / lead in context for each approval in the approvals list
processedCustomData = getTitle(approvals);
if (pluginData.size() > 0 && pluginData.get(key) != null && pluginData.get(key) != '') {
for (mcdm_15.JourneyApproval approval : approvals) {
Map<String, String> titleMap = new Map<String, String>();
// Here we build the custom title which is title of the contact / lead + first name + last name + the value passed in from front end component
// ex: Doctor John Doe, We are happy to have you!
// Doctor is the title of John Doe contact
// "We Are happy to have you!" is the custom text passed in
titleMap.put(key, processedCustomData.get(approval.objectId) + ' ' + approval.campaignMemberFields.get('FirstName') + ' ' + approval.campaignMemberFields.get('LastName') + ', ' + pluginData.get(key));
IdsToCustomData.put(approval.objectId, titleMap);
}
} else {
// If no input is provided from the front end component, show a default title
for (mcdm_15.JourneyApproval approval : approvals) {
Map<String, String> titleMap = new Map<String, String>();
titleMap.put(key, 'No Input from Bulk CPI');
IdsToCustomData.put(approval.objectId, titleMap);
}
}
// Return a map of approval object IDs to the custom data map of customSubjectLine -> value
return IdsToCustomData;
}
// In this example, we get the title for each contact
private static Map<String, String> getTitle(List<mcdm_15.JourneyApproval> approvals) {
Map<String, String> approvalsToTitle = new Map<String, String>();
List<String> contactIds = new List<String>();
List<String> leadIds = new List<String>();
// Get the contact and lead IDs from the JourneyApproval records passed in
for (mcdm_15.JourneyApproval approval : approvals) {
if (approval.objectType == Schema.SObjectType.Contact.getName()) {
contactIds.add(approval.objectId);
} else if (approval.objectType == Schema.SObjectType.Lead.getName()) {
leadIds.add(approval.objectId);
}
}
if (contactIds.size() > 0) {
if (Limits.getQueries() == Limits.getLimitQueries()) {
// Example of throwing a custom exception to handle govener limits in this custom plugin class
throw new mcdm_15.BulkSendCustomPluginException('SOQL limit of ' + Limits.getLimitQueries() + ' reached.');
}
// Get the title and return the map of contactId to title
List<Contact> contacts = [SELECT Id, Title FROM Contact WHERE Id IN :contactIds];
for (Contact contact : contacts) {
approvalsToTitle.put(contact.Id, contact.Title == null ? '' : contact.Title);
}
}
if (leadIds.size() > 0) {
if (Limits.getQueries() == Limits.getLimitQueries()) {
// Example of throwing a custom exception to handle govener limits in this custom plugin class
throw new mcdm_15.BulkSendCustomPluginException('SOQL limit of ' + Limits.getLimitQueries() + ' reached.');
}
// Get the title and return the map of contactId to title
List<Lead> leads = [SELECT Id, Title FROM Lead WHERE Id IN :leadIds];
for (Lead lead : leads) {
approvalsToTitle.put(lead.Id, lead.Title == null ? '' : lead.Title);
}
}
return approvalsToTitle;
}
}
Here, the Aura Component is BulkSendCustomTitleCPI.cmp.
<aura:component access="GLOBAL" implements="mcdm_15:MessagePersonalizationHandler" controller="BulkSendCustomTitleCPI">
<aura:attribute access="GLOBAL" name="personalizationReadyHandler" type="Aura.Action" default="{!c.messagePersonalizationReady}"/>
<aura:attribute name="customSubjectLine" access="private" type="String" description="Custom subject line used in bulk CPI logic"/>
<aura:attribute name="selectedMembers" access="private" type="Object[]" description="The currently selected member"/>
<aura:handler name="init" value="{!this}" action="{!c.init}"/>
<lightning:spinner variant="brand" size="large" aura:id="mySpinner"/>
<div class="slds-card">
<div class="slds-card__header">
<div class="slds-media slds-media_center">
<div class="slds-media__figure">
<lightning:icon iconName="custom:custom24" size="large"/>
</div>
<div class="slds-media__body">
<p class="slds-text-heading_small">Bulk CPI - Custom subject demo</p>
</div>
</div>
</div>
<div class="slds-card__body slds-card__body_inner">
<div>
<p class="slds-text-title slds-text-align_center">'{!v.selectedMembers.length}' user(s) selected</p>
</div>
<div class="slds-m-around_medium">
<lightning:input class="slds-m-bottom_small" label="customSubjectLine" value="{!v.customSubjectLine}" onchange="{!c.handleInputChanged}"/>
</div>
</div>
</div>
</aura:component>
Here, the Aura Component Controller is BulkSendCustomTitleCPI.js.
({
init: function(cmp) {
cmp.set('v.selectedMembers', []);
},
messagePersonalizationReady: function(cmp, event) {
var selectedMembers = event.getParam('arguments').members;
var customData = event.getParam('arguments').customData;
cmp.set('v.selectedMembers', selectedMembers);
if (selectedMembers.length) {
var data = selectedMembers[0].customData || {};
cmp.set('v.customSubjectLine', data.customSubjectLine || '');
} else if (customData != null) {
var data = customData.autosend || {};
cmp.set('v.customSubjectLine', data.customSubjectLine || '');
}
var spinner = cmp.find('mySpinner');
$A.util.addClass(spinner, 'slds-hide');
},
handleInputChanged: function(cmp) {
// Get the data set from the component in the modal to pass to the
// apex class for bulk send CPI, this will be appended to the logic
// performed in the apex class.
var data = {
customSubjectLine: cmp.get('v.customSubjectLine')
};
var selectedMembers = cmp.get('v.selectedMembers'),
id,
dataProvidedEvent;
if (selectedMembers.length) {
selectedMembers.forEach(function(member) {
dataProvidedEvent = $A.get('e.mcdm_15:JourneyApprovalsDataProvided');
// Handles campaign vs quick send object IDs for preview updates
id = member.campaignMemberId || member.objectId;
dataProvidedEvent.setParams({
id: id,
data: data
});
dataProvidedEvent.fire();
});
} else {
dataProvidedEvent = $A.get('e.mcdm_15:JourneyApprovalsDataProvided');
// Supports autosend flow
dataProvidedEvent.setParams({
id: 'autosend',
data: data
});
dataProvidedEvent.fire();
}
}
})
Register using this anonymous Apex code.
SObject mapping = new mcdm_15__JourneyApprovalsPlugin__c(
mcdm_15__Component_Name__c='c:BulkSendCustomTitleCPI',
mcdm_15__ApexClassName__c = 'BulkSendCustomTitleCPI',
mcdm_15__IsBulkSendPlugin__c = true);
insert mapping;