This post will talk through how to setup a custom actions to be executed to update Sitecore contact through FXM API. If you don’t know about FXM you can read a brief overview about it in the Sitecore site and the doc site
If you’ve played around with FXM and it’s javascript API then you would know that you can trigger client side call to send information back to Sitecore regarding the current visitor browsing behaviour. But what does one do if we want to update the Contact information instead? say the current visitor email address if they fill out a newsletter subscription form? or their unique user id if they have logged in through the external application?
Looking at the available javascript FXM API there’s these three methods available:
- SCBeacon.trackEvent
- SCBeacon.trackGoal
- SCBeacon.trackOutcome
and there’s no such things as SCBeacon.updateContact which we sought after. After some investigation there’s a couple of ways to do this.
1. A custom web service call to the Sitecore server
Setup a custom web service in Sitecore server and pass in the current visitor contact id for custom processing. Doable but I’m looking for something that can be done through the same javascript API if possible as it would mean greater coverage – can be reused by other external site. By doing it through FXM javascript API we don’t have to setup web service call in each external sites.
2. Modify the beacon script to include our custom function
Modify the beacon script and include the custom SCBeacon.updateContact function. Figured that this won’t be ideal as we would muck around with the bundled javascript and won’t be compatible in Sitecore future upgrades.
3. Hook into one of the available methods to do some custom processing
This seems like the best place to start. Out of the available methods I choose to override the trackEvent tracking implementation because the action that I want to captured falls to “system” type action, doesn’t relate to conversion (Goal) or has monetary value (Outcome). Plus if I use page events then by default the events triggered will not show in the Experience Profile which might confused the marketers.
OK, let’s do this.
This is done with the latest Sitecore version at the time of writing which is 8.1 update 3.
First let’s open the Sitecore.FXM.config in /website/app_config/include/fxm folder
1 2 3 4 5 6 7 8 |
<tracking.triggerpageevent> <!-- Run when a page event is triggered by the client javascript to record the event in the DMS in the current session. --> <processor type="Sitecore.FXM.Pipelines.Tracking.BeforeEvent.InitializeTrackingCookieProcessor, Sitecore.FXM" /> <processor type="Sitecore.FXM.Pipelines.Tracking.BeforeEvent.InitializeExternalTrackingProcessor, Sitecore.FXM" /> <processor type="Sitecore.FXM.Pipelines.Tracking.BeforeEvent.EnsureCurrentPageIsTrackedProcessor, Sitecore.FXM" /> <processor type="Sitecore.FXM.Pipelines.Tracking.TriggerPageEvent.RunRegisterPageEventProcessor, Sitecore.FXM" /> <processor type="Sitecore.FXM.Pipelines.Tracking.AfterEvent.CleanupAnalyticsCookieInResponseProcessor, Sitecore.FXM" /> </tracking.triggerpageevent> |
I opened up Sitecore.FXM.Pipelines.Tracking.TriggerPageEvent.RunRegisterPageEventProcessor using dotPeak to find out that it will run the tracking.registerpageevent pipeline
1 2 3 4 5 |
<tracking.registerpageevent> <!-- Performs the registration of a page event in the DMS. --> <processor type="Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent.EnsureEventItemExistsProcessor, Sitecore.FXM" /> <processor type="Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent.RegisterPageEventProcessor, Sitecore.FXM" /> </tracking.registerpageevent> |
When I take a look at Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent.RegisterPageEventProcessor and what it does it seems this would be the best place to put my custom logic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
namespace Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent { public class RegisterPageEventProcessor : IRegisterPageEventProcessor, IRegisterPageEventProcessor<RegisterPageEventArgs> { public void Process(RegisterPageEventArgs args) { Assert.ArgumentNotNull((object) args, "args"); Assert.IsNotNull((object) args.PageEventItem, "No item has been found corresponding to the page event."); Assert.IsNotNull((object) args.CurrentPage, "The current page is not tracked in the current session. No events can be triggered."); switch (args.EventParameters.EventType) { case PageEventType.Goal: case PageEventType.Event: this.TriggerPageEvent(args); break; case PageEventType.Campaign: this.TriggerCampaign(args); break; case PageEventType.Outcome: this.TriggerOutcome(args); break; case PageEventType.Element: this.TriggerElement(args); break; } } protected virtual void TriggerPageEvent(RegisterPageEventArgs args) { PageEventItem pageEventItem = new PageEventItem(args.PageEventItem); PageEventData pageData = new PageEventData(pageEventItem.Name, pageEventItem.ID.Guid) { Data = args.EventParameters.Data, DataKey = args.EventParameters.DataKey, Text = this.ToQueryString(args.EventParameters.Extras) }; args.CurrentPage.Register(pageData); } |
We can override the TriggerPageEvent method and implement our custom processing.
The next step is for us to create our custom Processor class which extends from Sitecore’s default RegisterPageEventProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
using System; using System.Linq; using Sitecore.Analytics; using Sitecore.Analytics.Model.Entities; using Sitecore.Analytics.Tracking; using Sitecore.Diagnostics; using Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent; namespace Sitecore.POC.FXM.Pipelines.Tracking.RegisterPageEvent { public class RegisterPageEventProcessor : Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent.RegisterPageEventProcessor { protected override void TriggerPageEvent(RegisterPageEventArgs args) { // let the base method do it's stuff and register the page event base.TriggerPageEvent(args); // here we are trying to update the current contact data if (Tracker.Current != null && Tracker.Current.Contact != null) { if (string.Equals(args.PageEventItem.Name, "updatePersonalInfo", StringComparison.OrdinalIgnoreCase)) { UpdatePersonalInfo(Tracker.Current.Contact, args); } else if (string.Equals(args.PageEventItem.Name, "identifyContact", StringComparison.OrdinalIgnoreCase)) { IdentifyContact(args); } else if (string.Equals(args.PageEventItem.Name, "updateEmail", StringComparison.OrdinalIgnoreCase)) { UpdateEmail(Tracker.Current.Contact, args); } } else { Log.Info("POC: cannot found the contact for the current interaction", this); } } private void UpdateEmail(Contact contact, RegisterPageEventArgs args) { var contactEmailAddresses = contact.GetFacet<IContactEmailAddresses>("Emails"); if (contactEmailAddresses != null && string.Equals(args.EventParameters.DataKey, "email", StringComparison.OrdinalIgnoreCase)) { if (contactEmailAddresses.Entries.Contains("Preferred")) { var preferredEmailAddress = contactEmailAddresses.Entries["Preferred"]; preferredEmailAddress.SmtpAddress = args.EventParameters.Data; } else { var preferredEmailAddress = contactEmailAddresses.Entries.Create("Preferred"); preferredEmailAddress.SmtpAddress = args.EventParameters.Data; } } } private void UpdatePersonalInfo(Contact contact, RegisterPageEventArgs args) { var personalInfo = contact.GetFacet<IContactPersonalInfo>("Personal"); if (personalInfo != null && args.EventParameters.Extras.Any()) { var values = args.EventParameters.Extras; if (values.ContainsKey("FirstName")) { personalInfo.FirstName = values["FirstName"]; } if (values.ContainsKey("Gender")) { personalInfo.Gender = values["Gender"]; } } } private void IdentifyContact(RegisterPageEventArgs args) { if (string.Equals(args.EventParameters.DataKey, "id", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(args.EventParameters.Data)) { Tracker.Current.Session.Identify(args.EventParameters.Data); } } } } |
After that we need to register our custom processor class in the config file. For best practice use Sitecore config file patching to do.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <group groupName="FXM" name="FXM"> <pipelines> <tracking.registerpageevent> <!-- Performs the registration of a page event in the DMS. --> <processor type="Sitecore.POC.FXM.Pipelines.Tracking.RegisterPageEvent.RegisterPageEventProcessor, Sitecore.POC" patch:instead="processor[@type='Sitecore.FXM.Pipelines.Tracking.RegisterPageEvent.RegisterPageEventProcessor, Sitecore.FXM']"/> </tracking.registerpageevent> </pipelines> </group> </pipelines> </sitecore> </configuration> |
In our custom config file we override the default processor class to process the triggered page events with our custom processor class. Put the file in /website/app_config/include/zzz.POC/ folder
Now that we have our custom processor class and have registered it through the custom config file, we need to create the actual page event items in Sitecore.
Here’s what the created page event items looks like in my setup.
And now to test it out, browse to the external site and open the developer console to run these javascripts
1 |
SCBeacon.trackEvent('updatePersonalInfo',{xFirstName:'Sarah',xGender: 'female'}) |
1 |
1 |
We can check the end result in MongoDB after the session has timeout
And in the Experience Profile
That’s it folks.