Versions
Sitecore 8 Update 2
EXM 3 rev 150223
Tasks
Create a custom facet with fields on a contact
Segment a List Manager Segmented List with custom rules using the custom facet fields
Details
In Sitecore, using the List Manager, you can create Segmented Lists segmented with rules. These segmented lists can be used with EXM (formerly known as ECM) to send email messages.
Sitecore List Manager Segmented List builder with rules
Image may be NSFW.
Clik here to view.
We’ll start by creating a custom facet with fields with a custom program to import a recipient list and create or update contacts for the list as described here.
Create custom facet field
Adam Conn describes how to add the custom contact facet and fields
Here’s a code snippet to add the facet with fields we’ll use for segmentation rules
// Custom Facet fields: contactType, salutation, age var eXMContactFacetNew = newContact.GetFacet<IEXMContact>(EXMContactConstants.FACET_NAME); eXMContactFacetNew.ContactType = "Employer"; eXMContactFacetNew.Salutation = "Ms."; eXMContactFacetNew.Age = 22;
Facet Type
public interface IEXMContact : IFacet { string ContactType { get; set; } string Salutation { get; set; } int Age { get; set; } }
Facet in Mongo Db Contact
Image may be NSFW.
Clik here to view.
To use the custom facet fields in a rule action, we need to index the fields. Here is the configuration and computed index code to do that:
/App_config/include/EXMCustomContactData.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <model> <elements> <element interface="LaunchSitecore.Models.EXM.IEXMContact, LaunchSitecore" implementation="LaunchSitecore.Models.EXM.EXMContact, LaunchSitecore" /> </elements> <entities> <contact> <facets> <facet name="EXMContact" contract="LaunchSitecore.Models.EXM.IEXMContact, LaunchSitecore" /> </facets> </contact> </entities> </model> <!--Custom index field definition--> <contentSearch> <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch"> <indexes hint="list:AddIndex"> <index id="sitecore_analytics_index" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider"> <param desc="name">$(id)</param> <param desc="folder">$(id)</param> <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" /> <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration"> <fieldMap ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration/fieldMap"> <fieldNames hint="raw:AddFieldByFieldName"> <field fieldName="contact.EXMContact.Age" type="System.Int32" storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" /> <field fieldName="contact.EXMContact.ContactType" type="System.String" storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" /> <field fieldName="contact.EXMContact.Salutation" type="System.String" storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" /> </fieldNames> </fieldMap> <fields hint="raw:AddComputedIndexField"> <field fieldName="contact.EXMContact.Age" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_Age, LaunchSitecore" matchField="type" matchValue="contact"/> <field fieldName="contact.EXMContact.ContactType" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_ContactType, LaunchSitecore" matchField="type" matchValue="contact"/> <field fieldName="contact.EXMContact.Salutation" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_Salutation, LaunchSitecore" matchField="type" matchValue="contact"/> </fields> </configuration> </index> </indexes> </configuration> </contentSearch> <!--End of custom index field definition--> </sitecore> </configuration>
This includes an example of an integer (“Age”) and strings (“ContactType,”Salutation”).
The computed index code example from “Age” with “ContactType” and “Salutation” similar:
public class EXMContact_Fields_Indexing_Age : IComputedIndexField { public object ComputeFieldValue(IIndexable indexable) { var contactIndexable = indexable as ContactIndexable; if (contactIndexable != null) { ContactRepositoryBase contactRepositoryBase = Factory.CreateObject("contactRepository", true) as ContactRepositoryBase; if (contactRepositoryBase != null) { var contact = contactRepositoryBase.LoadContactReadOnly((Guid)contactIndexable.Id.Value); var contactData = contact.GetFacet<IEXMContact>(EXMContactConstants.FACET_NAME); return contactData.Age; } } return null; } public string FieldName { get; set; } public string ReturnType { get; set; } }
The segmented rule is added to /sitecore/system/Settings/Rules/Definitions/Elements/Segment BuilderImage may be NSFW.
Clik here to view.
Age
Text
where the contact age [operatorid,Operator,,compares to] [Age,PositiveInteger,defaultValue=&validationText=Please enter a valid age.,age]
Type
LaunchSitecore.Extensions.EXM.Rules.ContactAgeCondition, LaunchSitecore
(your type)
Contact Type
Text
where the contact type [operatorid,StringOperator,,compares to] [value,,,specific contacttype]
Type
Here’s the code for the rules demonstrating the integer Age and string Contact Type
public class ContactAgeCondition<T> : TypedQueryableOperatorCondition<T, IndexedContact> where T : VisitorRuleContext<IndexedContact> { public ContactAgeCondition() { this.Age = Int32.MinValue; } protected override Expression<Func<IndexedContact, bool>> GetResultPredicate(T ruleContext) { if (this.Age != Int32.MinValue) { // This is a GEM of a snippet to get the integer value return base.GetCompareExpression<int>(c => (int)c[(ObjectIndexerKey)"contact.exmpbscontact.age"], this.Age); } return c => false; } //Properties public int Age { get; set; } } public class ContactTypeCondition<T> : TypedQueryableStringOperatorCondition<T, IndexedContact> where T: VisitorRuleContext<IndexedContact> { protected override Expression<Func<IndexedContact, bool>> GetResultPredicate(T ruleContext) { return base.GetCompareExpression(c => c["contact.exmpbscontact.contacttype"], base.Value); } }
And that’s all you need to do to use the new rules to segment a list by custom facet fields in a contact.
Image may be NSFW.
Clik here to view.
Clik here to view.
