Sitecore Custom Contact Facets

November 22, 2016

Blog | Development | Sitecore Custom Contact Facets

Recently I ran into a situation where I wanted to store a users interaction with the current page and be able to access it at a much later date.  Just to give a more concrete example of what I was trying to achieve and how to achieve it:  on a project I have been working on, on their homepage, there is a Help Message component that will show on the page until either the message expires or the user clicks the Don't show me this anymore button on the message.  I was kindly introduced to the concept of storing facets on the Sitecore Contact item and I figured this was the perfect place for me to store this interaction, outside of session, on the current user itself.  The first step of leveraging custom facets on the contact is to define an interface to act as the contract item:

namespace YourProject.Namespace.ContactFacets
{
  public interface IHiddenHelpMessage : IElement
  {
    DateTime TimeHidden { get; set; }
    Guid ItemHidden { get; set; }
  }

  public interface IHiddenHelpMessages : IFacet
  {
    IElementCollection Messages { get; }
  }
}

In the snippet above, you will see 2 basic interfaces, IHiddenHelpMessage and IHiddenHelpMessages. IHiddenHelpMessage is the child object that will hold the actual data we want (in our case the item ID of the item being hidden and the timestamp of when it was chosen to be hidden) and should inherit from IElement. The second interface, IHiddenHelpMessages, is the object that will hold the collection (IElementCollection is a faceting specific collection class) of the child IHiddenHelpMessage items and it should inherit from the `IFacet`. Now that we have interfaces defined, we will want to define the concrete classes that will be bound to these interfaces. We can use the example below:

namespace YourProject.Namespace.ContactFacets
{
  [Serializable]
  public class HiddenMessage : Element, IHiddenHelpMessage
  {
    private const string TIME_HIDDEN = "TimeHidden";
    private const string ITEM_HIDDEN = "ItemHidden";

    public DateTime TimeHidden
    {
      get
      { 
	    return this.GetAttribute(TIME_HIDDEN);
      }
      set
      { 
	    this.SetAttribute(TIME_HIDDEN, value);
      }
    }

    public Guid ItemHidden
    {
      get
      { 
	    return this.GetAttribute(ITEM_HIDDEN);
      }
      set
      { 
	    this.SetAttribute(ITEM_HIDDEN, value);
      }
    }

    public HiddenMessage() {
      this.EnsureAttribute(TIME_HIDDEN);
      this.EnsureAttribute(ITEM_HIDDEN);
    }
  }

  [Serializable]
  public class HiddenMessages : Facet, IHiddenHelpMessages {
    private const string FIELD_HIDDEN_HELP_MESSAGES = "HiddenHelpMessages";
    public HiddenMessages()
    { 
      this.EnsureCollection(FIELD_HIDDEN_HELP_MESSAGES); 
    }

    public IElementCollection Messages {
      get
      { 
        return this.GetCollection(FIELD_HIDDEN_HELP_MESSAGES);
      }
    }
  }
}

The above snippet should illustrate a handful of things: have a class that implements the previous interfaces, have those classes inherit from Element and Facet respectively, use this.GetAttribute(key_string) and this.SetAttribute(key_string, value) to get and set the property values, and have the constructor ensure all properties (this.EnsureAttribute(key_string) or this.EnsureCollection(FIELD_HIDDEN_HELP_MESSAGES)). Alright, now that we have the contract objects setup, we need to tell Sitecore how and where to access them, we can do so by patching the Sitecore.Analytics.Model.config like the following:


<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <model>
      <elements>
        <element patch:after="element[@interface='Sitecore.Analytics.Model.Entities.IContactPreferences, Sitecore.Analytics.Model']" interface="YourProject.Namespace.ContactFacets.IHiddenMessage, YourProject" implementation="YourProject.Namespace.ContactFacets.HiddenMessage, YourProject" />
        <element patch:after="element[@interface='Sitecore.Analytics.Model.Entities.IContactPreferences, Sitecore.Analytics.Model']" interface="YourProject.Namespace.ContactFacets.IHiddenMessages, YourProject" implementation="YourProject.Namespace.ContactFacets.HiddenMessages, YourProject" />
      </elements>
      <entities>
        <contact>
          <facets>
            <facet patch:after="facet[@name='Preferences']" name="Hidden Messages" contract="YourProject.Namespace.ContactFacets.IHiddenMessages, YourProject" />
          </facets>
        </contact>
      </entities>
    </model>
  </sitecore>
</configuration>

The config snippet above does 2 things, tells Sitecore what class to implement given the interfaces and associates the facet key that this will be stored as. At this point, Sitecore is completely aware of our facet, so for the last and most important thing: how to access and create. Accessing the facets will be done thru the Tracker.Current.Contact.GetFacet<I>(facet_key):


  var hiddenMessages = Tracker.Current.Contact.GetFacet("Hidden Messages");
  var allHiddenMessages = hiddenMessages.Messages;

Now creating and adding facet items to the contact is less straightforward: the IElementCollection has a Create() method that will create and add a new empty instance and return a handle to it. The below snippet may help illustrate usage:

  var newHiddenMessage = hiddenMessages.Messages.Create();
  newHiddenMessage.ItemHidden = itemId;
  newHiddenMessage.TimeHidden = timestamp;

That’s essentially it!  You can now store data programmatic on the Contact itself as a facet in a Sitecore-idiomatic way.

Steve VandenBush

Technical Lead
Tags
  • faceting
  • Personalization
  • Sitecore
subscribe to GeekHive's newsletter

Never miss out on a Sitecore update again!

Our newsletter features a blog roundup of our top posts so you can stay up to date with industry trends, tutorials, and best practices.

Recent Work

Check out what else we've been working on