Bringing goal triggering and engagement plans together

Engagement plans

This blog focuses on Sitecore 8.2.x but may still apply to Automation plans in Sitecore 9, although code samples will not work, the general idea remains the same.

With this implementation I aimed to enable marketers to trigger engagement plans upon goal triggering in their website. It is already possible out of the box to enroll a visitor in an engagement plan on WFFM form submission, manually or through EXM campaigns. However I wanted to enable our marketers to have more flexibility. For this I designed a mechanism that allows a marketer to hook up Sitecore goals to engagement plans. In this way a marketer can select an engagement plan and state that a user should be enrolled in upon triggering a Sitecore goal. 


Item definitions 

I started by making templates to enable the marketer in selecting his Sitecore goals and engagement plans to enroll the visitor in.


With this setup there is now a connection between goals and plans. A marketer can even select multiple goals that need to be triggered before a user is enrolled in a specific plan and state within the plan

Automatic enrollment

To enable the above logic, I needed to create a custom processor that is triggered immediately after a goal has been triggered. I patched it in a config file:

 <?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
   <sitecore>
    <pipelines>
      <registerPageEvent>
        <processor patch:after="*[@type='Sitecore.Analytics.Pipelines.RegisterPageEvent.RunPageEventRules, Sitecore.Analytics']"
                   type="Custom.Site.Processors.RegisterPageEventProcessor, Custom.Site" />
      </registerPageEvent>
    </pipelines>
  </sitecore>
</configuration>

The customer processor I made iterates over all engagement plans and checks whether the goal that was just triggered was part of the engagement plan configurations. If so, and all goals that are required to enroll in the plan have been triggered, the enrollment is done.

    /// <summary>
    /// This processor checks whether page event is a Goal, and if so, checks whether any engagement plan is configured to be triggered upon this goal trigger. 
    /// If so, contact is enrolled in the plan
    /// </summary>
    public class RegisterPageEventProcessor
    {
        public void Process(RegisterPageEventArgs args)
        {
            Assert.ArgumentNotNull(args, "args");

            if (args.Session != null && args.PageEvent != null)
            {
                var enrollmentItems = EngagementPlanHelper.EngagementPlanEnrollmentList.Where(epe =>
                        epe.GoalsThatAreRequiredToBeTriggered.Any(g => g.ID.Guid == args.PageEvent.PageEventDefinitionId));

                if (EngagementPlanHelper.EngagementPlanEnrollmentList != null && enrollmentItems != null && enrollmentItems.Any())
                {
                    foreach (var enrollmentItem in enrollmentItems)
                    {
                        EvaluateAndPossiblyEnroll(enrollmentItem, args.PageEvent.PageEventDefinitionId, args.Session);
                    }
                }
            }
        }

        private void EvaluateAndPossiblyEnroll(EngagementPlanEnrollment enrollmentItem, Guid pageEventDefinitionId, Session session)
        {
            bool allGoalsTriggered = false;

            foreach (var goalItem in enrollmentItem.GoalsThatAreRequiredToBeTriggered)
            {
                if (AnalyticsHelper.ContactHasTriggeredGoal(goalItem.ID.Guid, session))
                {
                    allGoalsTriggered = true;
                }
                else
                {
                    allGoalsTriggered = false;
                    break;
                }
            }

            if (allGoalsTriggered)
            {
                foreach (var engagementPlan in enrollmentItem.EngagementPlansUserWillBeEnrolledIn)
                {
                    var statetoStartIn = enrollmentItem.EngagementPlansStateToStartIn.FirstOrDefault(item => item.Parent.ID == engagementPlan.ID);
                    if (statetoStartIn != null)
                    {
                        AnalyticsHelper.EnrollUserInEngagementPlan(engagementPlan.ID, statetoStartIn.ID, session);
                    }
                }
            }
        }
    }



    /// <summary>
    /// Helper class to provide access to engagement plan enrollment Sitecore items
    /// </summary>
    public static class EngagementPlanHelper
    {
        /// <summary>
        /// Finds engagementplan enroll configuration item under the Sitecore configuration node of the active site context and adds all child enrollment items.
        /// </summary>
        /// <returns></returns>
        private static List<EngagementPlanEnrollment> GetPlanEnrollmentList()
        {
            var result = new List<EngagementPlanEnrollment>();
            Item configFolder = ContentHelper.GetSharedConfiguration();
            
            var containerFolder = configFolder.GetChildByTemplate(SitecoreConstants.EngagementPlanEnrollmentFolder.TemplateName);
            if (containerFolder != null && containerFolder.Children != null && containerFolder.Children.Any())
            {
                foreach (Item item in containerFolder.Children)
                {
                    if (item.TemplateName == Well.SitecoreLibrary.SitecoreConstants.EngagementPlanEnrollment.TemplateName)
                    {
                        result.Add(new EngagementPlanEnrollment(item));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Provides engagementplan enrollment list with goals that need to be triggered for an engagement plan to be triggered
        /// </summary>
        public static List<EngagementPlanEnrollment> EngagementPlanEnrollmentList
        {
            get
            {
                return GetPlanEnrollmentList();
            }
        }
    }
   /// <summary>
    /// Helper class to deal with Sitecore analytics items such as goals and campaigns
    /// </summary>
    public static class AnalyticsHelper
    {

        /// <summary>
        /// Check whether current session contact has triggered supplied goal in any of his recent interactions
        /// </summary>
        /// <param name="goalId"></param>
        /// <param name="session"></param>
        /// <returns></returns>
        public static bool ContactHasTriggeredGoal(Guid goalId, Session session)
        {
            //NOTE: KeyBehavior cache only contains configured number of goals for performance reasons, default setting is 50
            // Keybahvior cache ony scans period for interactions that xDB uses during cache rebuilds or updates. Default setting is 30 days
            // Keybahvior cache scans the maximum number of interactions that xDB examines during cache rebuilds or updates. Default setting is 25 interactions
            // To change settings, manipulate values: Xdb.Tracking.KeyBehaviorCache.MaximumCacheEntryCount, Xdb.Tracking.KeyBehaviorCache.MaximumInteractionPeriod,
            // Xdb.Tracking.KeyBehaviorCache.MaximumInteractionCount
            var keyBehaviorCache = session.Contact.GetKeyBehaviorCache();
            var goals = session.Interaction.Pages.SelectMany(i => i.PageEvents).Where(x => x.IsGoal);
            return keyBehaviorCache.Goals.Any(g => g.Id == goalId) || goals.Any(g => g.PageEventDefinitionId == goalId);
        }


        /// <summary>
        /// Enroll current context contact into specified engagement plan and at specific state in the engagementplan.
        /// </summary>
        /// <param name="engagementPlanId">Sitecore ID of engagementplan</param>
        /// <param name="engagementPanStartStateId">Sitecore ID of engagementplan state</param>
        /// <param name="session">Current Sitecore Analytics session</param>
        public static void EnrollUserInEngagementPlan(ID engagementPlanId, ID engagementPanStartStateId, Session session)
        {
            var managerInContext = session.CreateAutomationStateManager();
            if (!managerInContext.IsInEngagementPlan(engagementPlanId))
            {
                managerInContext.EnrollInEngagementPlan(engagementPlanId, engagementPanStartStateId);
                managerInContext.SaveChanges(AutomationManager.Provider);
            }
        }
}

Note that we can only compare to a limited amount of interactions triggered by the visitor in the past, due to performance reasons. So these use cases should preferably be limited to a combination of goals triggered in one session.

Reacties

Populaire posts van deze blog

I Robot - Sitecore JSS visitor identification

Sitecore campaigns and UTM tracking unified

Sitecore JSS - Sitecore first