Salesforce Field Values in Sitecore Facets for Rules Based Personalization

The following code provides ideas on how to pull Salesforce field values into Sitecore xDB facets for Sitecore 8.2. You may need to modify it for other versions.

First, the SitecoreConfigPatch.config file is used to register the custom facet:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <model>
      <elements>
        <element interface="FuseIT.S4S.WebToSalesforce.Facets.ISalesforceLeadFacet, FuseIT.S4S.WebToSalesforce" implementation="FuseIT.S4S.WebToSalesforce.Facets.SalesforceLeadFacet, FuseIT.S4S.WebToSalesforce" />
      </elements>
      <entities>
        <contact>
          <facets>
            <facet name="Salesforce Lead" contract="FuseIT.S4S.WebToSalesforce.Facets.ISalesforceLeadFacet, FuseIT.S4S.WebToSalesforce"/>
          </facets>
        </contact>
      </entities>
    </model>
  </sitecore>
</configuration>

The following sample code includes the classes:

  • ISalesforceLeadFacet – Define the interface for the custom facet
  • SalesforceLeadFacet – Implementation of the custom facet
  • Class1 – Code to do the following:
    • Query Salesforce, using S4S, for all leads that have a Sitecore visitor id and have been modified since the last sync
    • For each Salesforce Lead
      • Get the Sitecore visitor id and status
      • Get the associated contact from xDB
      • Update the facet with the status
      • Save the data back to xDB

The code assumes that the last sync date and time have been stored in the Sitecore tree. This value can be retrieved and used in the query to Salesforce and updated at the end of the current sync. There are other considerations when trying to get the contact such as other processes already having the contact locked.

using FuseIT.S4S.WebToSalesforce.Facets;
using FuseIT.Sitecore.SalesforceConnector;
using FuseIT.Sitecore.SalesforceConnector.DataSource;
using FuseIT.Sitecore.SalesforceConnector.Entities;
using Sitecore.Analytics.Data;
using Sitecore.Analytics.DataAccess;
using Sitecore.Analytics.Model;
using Sitecore.Analytics.Tracking;
using Sitecore.Configuration;
using System;
using System.Collections.Generic;

namespace FuseIT.S4S.WebToSalesforce
{
    /// <summary>
    /// Interface for the Salesforce Lead Facet
    /// </summary>
    public interface ISalesforceLeadFacet : IFacet
    {
        /// <summary>
        /// The Salesforce Lead status 
        /// </summary>
        string Status { get; set; }
    }

    /// <summary>
	/// Facet for Salesforce Lead
	/// </summary>
	[Serializable]
    public class SalesforceLeadFacet : Facet, ISalesforceLeadFacet
    {
        public static readonly string FacetName = "Salesforce Lead";
        private const string statusName = "Status";

        /// <summary>
        /// The Salesforce lead status
        /// </summary>
        public string Status
        {
            get
            {
                return this.GetAttribute<string>(statusName);
            }
            set
            {
                this.SetAttribute(statusName, value);
            }
        }

        /// <summary>
        /// Constructs a new instance of the SalesforceLeadFacet class
        /// </summary>
        public SalesforceLeadFacet()
        {
            this.EnsureAttribute<string>(statusName);
        }
    }

    public class Class1
	{
		public void SyncLeads()
		{
			DateTime syncStart = DateTime.UtcNow;

			DateTime lastSyncTime = new DateTime();
			string sitecoreVisitorIdFieldName = "FuseITAnalytics__SitecoreVisitorId__c";  // Note if using unmanaged package name will be SitecoreVisitorId__c
			string statusFieldName = "Status";

			// TODO Get last sync time from Sitecore tree

			// Query Salesforce to find all leads that have a Sitecore visitor id and have been modified since the last sync date.
			LeadDataSource leadDataSource = new LeadDataSource(new SalesforceSession(""));
			leadDataSource.AddDataSourceFilter(sitecoreVisitorIdFieldName, Sitecore.SalesforceConnector.Soql.ComparisonOperator.NotEquals, null);
			leadDataSource.AddDataSourceFilter("isConverted", Sitecore.SalesforceConnector.Soql.ComparisonOperator.Equals, false);
			leadDataSource.AddDataSourceFilter("LastModifiedDate", Sitecore.SalesforceConnector.Soql.ComparisonOperator.GreaterOrEqual, lastSyncTime);

			List<SObjectField> fields = new List<SObjectField>();
			fields.Add(new SObjectField(sitecoreVisitorIdFieldName));
			fields.Add(new SObjectField(statusFieldName));

			QueryResultPager pager = leadDataSource.GetPager(fields.ToArray());

			ContactRepositoryBase contactRepository = Factory.CreateObject("contactRepository", true) as ContactRepositoryBase;

			// For each Lead found update the status in Sitecore xDB
			for (int index = 0; index < pager.TotalRecordCount; index++)
			{
				Lead lead = leadDataSource.EntityFromQueryResultPager<Lead>(pager, index);

				string sitecoreVisitorId = lead.InternalFields[sitecoreVisitorIdFieldName];
				string leadStatus = lead.InternalFields[statusFieldName];

				Guid contactId;

				if (Guid.TryParse(sitecoreVisitorId, out contactId))
				{
					LeaseOwner leaseOwner = new LeaseOwner(GetType() + Guid.NewGuid().ToString(), LeaseOwnerType.OutOfRequestWorker);

					// Attempt to obtain an exclusive lock on an existing contact in xDB.
					LockAttemptResult<global::Sitecore.Analytics.Tracking.Contact> lockResult = contactRepository.TryLoadContact(contactId, leaseOwner, TimeSpan.FromMinutes(1));

					global::Sitecore.Analytics.Tracking.Contact contact = null;

					switch (lockResult.Status)
					{
						case LockAttemptStatus.AlreadyLocked:		// Another worker or a live session has an exclusive lock on the contact so contact cannot be used at the moment
							break;
						case LockAttemptStatus.DatabaseUnavailable: // Database is down
							break;
						case LockAttemptStatus.NotFound:            // Contact with identifier not found
							break;
						case LockAttemptStatus.Success:
							contact = lockResult.Object;
							break;
					}

					if (contact != null)
					{
						ISalesforceLeadFacet facet = contact.GetFacet<ISalesforceLeadFacet>(SalesforceLeadFacet.FacetName);

						if (facet != null)
						{
							facet.Status = leadStatus;
						}

						ContactSaveOptions options = new ContactSaveOptions(true, leaseOwner);
						contactRepository.SaveContact(contact, options);
					}
				}
			}

			// TODO Update last sync time in Sitecore tree with syncStart
		}
	}
}