Add Contact Id in Interactions for Non-Logged in Visitors

Hi Folks,

In previous post https://sitecorerocksblog.wordpress.com/2017/10/17/sitecore-web-forms-for-marketers-creating-custom-save-action-for-adding-contact-profile-for-non-logged-in-visitors/ we have added contact for non logged-in user in experience profile. Now we had a requirement to add same contact id in Interactions table as well.

Firstly I checked Interactions table in mongo db after submitting a form. For this I used Robomongo tool and checked Interactions table. I saw last entry in the table and saw that contact id is different in this entry then our contact id.

See below image for contact id (Contacts Table)-

ContactEntry

See below image for interaction entry (Interactions Table)-

InteractionsEntry

 

In above 2 images you can clearly see contact id is different. So, for this I searched on google and found a nice article of Brian Pedersen , so I took help from this post and added code for this in CustomUpdateContactDetails class (added this class in previous post to add contact id). I have added below line –

// Identify contact for interactions
if (!Tracker.IsActive)
return;
Tracker.Current.Session.Identify(Sitecore.Context.User.Name.ToLower() == “extranet\\anonymous”
? email
: Sitecore.Context.User.Name);

 

CustomUpdateContactDetails class including above code –

using Sitecore.Data;
using Sitecore.WFFM.Abstractions.Actions;
using Sitecore.WFFM.Actions.Base;
using Sitecore.Diagnostics;
using Sitecore.WFFM.Abstractions.Analytics;
using Sitecore.WFFM.Abstractions.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Script.Serialization;
using System.Web;
using Sitecore.Analytics;
using Sitecore.Analytics.Data;
using Sitecore.Analytics.DataAccess;
using Sitecore.Analytics.Model;
using Sitecore.Analytics.Tracking;
using Sitecore.Analytics.Tracking.SharedSessionState;
using Sitecore.Configuration;

namespace xyz.WFFMSaveAction
{
public class CustomUpdateContactDetails : WffmSaveAction
{
private const int _MAX_RETRIES = 10;
private int _retries;

private readonly IAnalyticsTracker analyticsTracker;

private readonly IAuthentificationManager authentificationManager;

private readonly ILogger logger;

private readonly IFacetFactory facetFactory;

public string Mapping
{
get;
set;
}

public CustomUpdateContactDetails(IAnalyticsTracker analyticsTracker, IAuthentificationManager authentificationManager, ILogger logger, IFacetFactory facetFactory)
{
Assert.IsNotNull(analyticsTracker, “analyticsTracker”);
Assert.IsNotNull(authentificationManager, “authentificationManager”);
Assert.IsNotNull(logger, “logger”);
Assert.IsNotNull(facetFactory, “facetFactory”);
this.analyticsTracker = analyticsTracker;
this.authentificationManager = authentificationManager;
this.logger = logger;
this.facetFactory = facetFactory;
}

public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
{
this.UpdateContact(adaptedFields);
}

protected virtual void UpdateContact(AdaptedResultList fields)
{
Assert.ArgumentNotNull(fields, “adaptedFields”);
Assert.IsNotNullOrEmpty(this.Mapping, “Empty mapping xml.”);
Assert.IsNotNull(this.analyticsTracker.CurrentContact, “Tracker.Current.Contact”);

var email = string.Empty;
foreach (AdaptedControlResult acr in fields)
{
if (acr.FieldName == “examplemailcom”)
{
email = acr.Value;
}
}

if (!this.authentificationManager.IsActiveUserAuthenticated)
{
var objContact = GetOrCreateContact(email);

IEnumerable<FacetNode> enumerable = this.ParseMapping(this.Mapping, fields);
IContactFacetFactory contactFacetFactory = this.facetFactory.GetContactFacetFactory();
foreach (FacetNode current in enumerable)
{
contactFacetFactory.SetFacetValue(objContact, current.Key, current.Path, current.Value, true);
}
ReleaseAndSaveContact(objContact);

// Identify contact for interactions
if (!Tracker.IsActive)
return;
Tracker.Current.Session.Identify(Sitecore.Context.User.Name.ToLower() == “extranet\\anonymous”
? email
: Sitecore.Context.User.Name);
}
}

public IEnumerable<FacetNode> ParseMapping(string mapping, AdaptedResultList adaptedFieldResultList)
{
Assert.ArgumentNotNullOrEmpty(mapping, “mapping”);
Assert.ArgumentNotNull(adaptedFieldResultList, “adaptedFieldResultList”);
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
object[] source = (object[])javaScriptSerializer.Deserialize(mapping, typeof(object));
return (from Dictionary<string, object> item in source
let itemValue = item[“value”].ToString()
let itemId = (item.ContainsKey(“id”) && item[“id”] != null) ? item[“id”].ToString() : “Preferred”
let value = adaptedFieldResultList.GetValueByFieldID(ID.Parse(item[“key”].ToString()))
where !string.IsNullOrEmpty(value)
select new FacetNode(itemId, itemValue, value)).ToList<FacetNode>();
}

public Sitecore.Analytics.Tracking.Contact GetOrCreateContact(string userName)
{
if (IsContactInSession(userName))
return Tracker.Current.Session.Contact;

ContactRepository contactRepository = Factory.CreateObject(“tracking/contactRepository”, true) as ContactRepository;
ContactManager contactManager = Factory.CreateObject(“tracking/contactManager”, true) as ContactManager;

Assert.IsNotNull(contactRepository, “contactRepository”);
Assert.IsNotNull(contactManager, “contactManager”);

try
{
Sitecore.Analytics.Tracking.Contact contact = contactRepository.LoadContactReadOnly(userName);
LockAttemptResult<Sitecore.Analytics.Tracking.Contact> lockAttempt;

lockAttempt = contact == null ? new LockAttemptResult<Sitecore.Analytics.Tracking.Contact>(LockAttemptStatus.NotFound, null, null) : contactManager.TryLoadContact(contact.ContactId);

return GetOrCreateContact(userName, lockAttempt, contactRepository, contactManager);
}
catch (Exception ex)
{
throw new Exception(this.GetType() + ” Contact could not be loaded/created – ” + userName, ex);
}
}

private bool IsContactInSession(string userName)
{
var tracker = Tracker.Current;

if (tracker != null &&
tracker.IsActive &&
tracker.Session != null &&
tracker.Session.Contact != null &&
tracker.Session.Contact.Identifiers != null &&
tracker.Session.Contact.Identifiers.Identifier != null &&
tracker.Session.Contact.Identifiers.Identifier.Equals(userName, StringComparison.InvariantCultureIgnoreCase))
return true;

return false;
}

private Sitecore.Analytics.Tracking.Contact GetOrCreateContact(string userName, LockAttemptResult<Sitecore.Analytics.Tracking.Contact> lockAttempt, ContactRepository contactRepository, ContactManager contactManager)
{
switch (lockAttempt.Status)
{
case LockAttemptStatus.Success:
Sitecore.Analytics.Tracking.Contact lockedContact = lockAttempt.Object;
lockedContact.ContactSaveMode = ContactSaveMode.AlwaysSave;
return lockedContact;

case LockAttemptStatus.NotFound:
Sitecore.Analytics.Tracking.Contact createdContact = CreateContact(userName, contactRepository);
contactManager.FlushContactToXdb(createdContact);
// Avoid going into an infinite loop.
_retries++;
if (_retries >= _MAX_RETRIES)
throw new Exception(string.Format(“ExtendedContactRepository: Contact {0} could not be created. “, userName));
return GetOrCreateContact(userName);

default:
throw new Exception(this.GetType() + ” Contact could not be locked – ” + userName);
}
}

private Sitecore.Analytics.Tracking.Contact CreateContact(string userName, ContactRepository contactRepository)
{
Sitecore.Analytics.Tracking.Contact contact = contactRepository.CreateContact(ID.NewID);
contact.Identifiers.Identifier = userName;
contact.Identifiers.IdentificationLevel = Sitecore.Analytics.Model.ContactIdentificationLevel.Known;
contact.System.Value = 0;
contact.System.VisitCount = 0;
contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
return contact;
}

public void ReleaseAndSaveContact(Sitecore.Analytics.Tracking.Contact contact)
{
ContactManager manager = Factory.CreateObject(“tracking/contactManager”, true) as ContactManager;
if (manager == null)
throw new Exception(this.GetType() + ” Could not instantiate ” + typeof(ContactManager));
manager.SaveAndReleaseContact(contact);
ClearSharedSessionLocks(manager, contact);
}

private void ClearSharedSessionLocks(ContactManager manager, Sitecore.Analytics.Tracking.Contact contact)
{
if (HttpContext.Current != null && HttpContext.Current.Session != null)
return;

var sharedSessionStateManagerField = manager.GetType().GetField(“sharedSessionStateManager”, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
Assert.IsNotNull(sharedSessionStateManagerField, “Didn’t find field ‘sharedSessionStateManager’ in type ‘{0}’.”, typeof(ContactManager));
var sssm = (SharedSessionStateManager)sharedSessionStateManagerField.GetValue(manager);
Assert.IsNotNull(sssm, “Shared session state manager field value is null.”);

var contactLockIdsProperty = sssm.GetType().GetProperty(“ContactLockIds”, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
Assert.IsNotNull(contactLockIdsProperty, “Didn’t find property ‘ContactLockIds’ in type ‘{0}’.”, sssm.GetType());
var contactLockIds = (Dictionary<Guid, object>)contactLockIdsProperty.GetValue(sssm);
Assert.IsNotNull(contactLockIds, “Contact lock IDs property value is null.”);
contactLockIds.Remove(contact.ContactId);
}
}
}

 

Now you can see in below image contact id is same.

interactionEntry2

And that’s it. So now we have added contact in experience profile as well as added same contact id in interactions table, that was our requirement.

Happy coding 🙂

 

One thought on “Add Contact Id in Interactions for Non-Logged in Visitors

Leave a comment