allBlogsList

Adding a custom contact facet in Sitecore

Recently, I have been working on a requirement where the client wanted to track the media download events and add this data to xDB. The best suitable approach for this would be to associate these events as a custom contact facet. I am planning to post a thread of articles which will address this requirement one blog post at a time. In this blog post, let’s start by defining our custom contact facet.

MediaDownloadsfacetinXDB

What is a Contact?

A contact is an individual who is interacting with your website. The contact, or contact entity contains all the information that you collect about an individual based on their interactions across websites.

What is a Facet?

A facet is a group of hierarchically organized, related attributes that describe a certain aspect of a contact. Facets are the smallest unit of attributes that can be added or removed from a contact during configuration.

What is a Custom Contact Facet?

We create custom contact facets to store data specific to your organization. These facets are treated the same way as standard facets. They are fully integrated into the contact and can be accessed anywhere in the system.

For my requirement, I need to have a collection of Media Downloads events which can store the following information

  • MediaId: a unique identifier
  • MediaName: Name of the media documents
  • MediaURL: The URL for the media item
  • Referrer: URL of the client's previous request that linked to the current URL
  • Time: Time when the document was requested
  • DateTime: Date time when the document was requested

Step 1: Create a Contract to define the structure of elements or facets

An element contract can be defined by implementing an interface which extends IElement interface. Our custom element holds the information mentioned above

    public interface IMediaDownloadElement : IElement
    {
        Guid MediaId { get; set; }
        string MediaName { get; set; }
        string MediaURL { get; set; }
        string Referrer { get; set; }
        string Time { get; set; }
        DateTime DateTime { get; set; }
    }

A facet contract can be defined by implementing an interface which extends IFacet interface. Our custom facet will hold the collection of elements

    public interface IMediaDownloadsFacet : IFacet
    {
        IElementCollection Downloads { get; }
    }

Step 2: Contract Implementation

We will create concrete classes which will be implementing our IMediaDownloadElement and IMediaDownloadsFacet

namespace XC.Reporting.Facets.Implementation
{
    [Serializable]
    public class MediaDownloadElement : Element, IMediaDownloadElement
    {
        private const string MEDIAID = "MediaId";
        private const string MEDIANAME = "MediaName";
        private const string MEDIAURL = "MediaURL";
        private const string REFERRER = "Referrer";
        private const string TIME = "Time";
        private const string DATETIME = "DateTime";

        public MediaDownloadElement()
        {
            EnsureAttribute(MEDIAID);
            EnsureAttribute(MEDIANAME);
            EnsureAttribute(MEDIAURL);
            EnsureAttribute(REFERRER);
            EnsureAttribute(TIME);
            EnsureAttribute(DATETIME);
        }

        public Guid MediaId
        {
            get
            {
                return GetAttribute(MEDIAID);
            }
            set
            {
                SetAttribute(MEDIAID, value);
            }
        }

        public string MediaName
        {
            get
            {
                return GetAttribute(MEDIANAME);
            }
            set
            {
                this.SetAttribute(MEDIANAME, value);
            }
        }

        public string MediaURL
        {
            get
            {
                return GetAttribute(MEDIAURL);
            }
            set
            {
                SetAttribute(MEDIAURL, value);
            }
        }

        public string Referrer
        {
            get
            {
                return GetAttribute(REFERRER);
            }
            set
            {
                SetAttribute(REFERRER, value);
            }
        }

        public string Time
        {
            get
            {
                return GetAttribute(TIME);
            }
            set
            {
                SetAttribute(TIME, value);
            }
        }

        public DateTime DateTime
        {
            get
            {
                return GetAttribute(DATETIME);
            }
            set
            {
                SetAttribute(DATETIME, value);
            }
        }
    }
}
namespace XC.Reporting.Facets.Implementation
{
    [Serializable]
    public class MediaDownloadsFacet : Facet, IMediaDownloadsFacet
    {
        private const string DOWNLOADS = "Downloads";

        public MediaDownloadsFacet()
        {
            EnsureCollection(DOWNLOADS);
        }

        public IElementCollection Downloads
        {
            get
            {
                return GetCollection(DOWNLOADS);
            }
        }
    }
}

We need to make sure these classes are marked with [Serializable] attribute. This will ensure that the instance of this calls can be stored in shared session and in the submit queue.

Step 3: Configuration

Add an entry in your custom config file where we will patch Sitecore.Analytics.Model.config for Sitecore to use the new facet and element

<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="XC.Reporting.Facets.Contracts.IMediaDownloadElement, XC.Reporting" implementation="XC.Reporting.Facets.Implementation.MediaDownloadElement, XC.Reporting"></element>
        <element patch:after="element[@interface='Sitecore.Analytics.Model.Entities.IContactPreferences, Sitecore.Analytics.Model']" interface="XC.Reporting.Facets.Contracts.IMediaDownloadsFacet, XC.Reporting" implementation="XC.Reporting.Facets.Implementation.MediaDownloadsFacet, XC.Reporting"></element>      
      </elements>
      <entities>
		    <contact>
			    <facets>
				    <facet patch:after="facet[@name='Preferences']" name="MediaDownloads" contract="XC.Reporting.Facets.Contracts.IMediaDownloadsFacet, XC.Reporting"></facet>
			    </facets>
		    </contact>
      </entities>
    </model>  
  </sitecore>
</configuration>

Step 4: How to access the facets?

IMediaDownloadsfacet mediaDownloads = contact.GetFacet("MediaDownloads")

Step 4: How to add new elements to the facet?

IMediaDownloadsfacet mediaDownloads = contact.GetFacet("MediaDownloads")
var download = mediaDownloads.Downloads.Create();

download.MediaId = Guid.NewGuid();
download.MediaName = "some value";
download.MediaURL = "some value";
download.Time = DateTime.Now.ToShortTimeString();
download.DateTime = DateTime.Now.ToLocalTime();
download.Referrer = "some value";

This has been implemented and tested on Sitecore 8.2.3.

Stay tuned for the next articles on how we make use of this custom contact facet to help with:

  • Track Media Download Events
  • How to Save and Release Contact to xDB
  • Merge Contact Facets
  • Reporting using Mongo Queries

Files:


Important Notes:

  • Make sure you add [Serializable] attribute during contract implementation
  • Make sure you name the Property the same as the value of the constant that you set for it
  • Validate your config entries by checking them under ~/sitecore/admin/showconfig.aspx

References: