How to display contact facet simple properties
End result:
There’s a good reference on how to create a custom tab in Sitecore Experience Profile on Jonathan Robbins blog post, I recommend you go and read it through.
How to set this up
- Setup a custom contact facet
- Setup a web API controller to return the custom contact facet as JSON
- Setup a custom tab in Experience Profile
- Wire things together
Source code link is available at the end of this post.
How to setup the custom contact facet
There’s a good reference on Sitecore documentation site on how to do so.
In my custom facet I only have two properties, which is the HospitalName and the ProfessionName.
ICustomFieldsFacet.cs
1 2 3 4 5 6 7 8 9 10 |
using Sitecore.Analytics.Model.Framework; namespace Sitecore.TC.ExperienceProfile.ContactFacets { public interface ICustomFieldsFacet : IFacet { string HospitalName { get; set; } string ProfessionName { get; set; } } } |
CustomFieldsFacet.cs
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 |
using System; using Sitecore.Analytics.Model.Framework; namespace Sitecore.TC.ExperienceProfile.ContactFacets { [Serializable] public class CustomFieldsFacet : Facet, ICustomFieldsFacet { public const string FACET_NAME = "CustomFields"; private const string HOSPITAL_NAME = "HospitalName"; private const string PROFESSION_NAME = "ProfessionName"; public CustomFieldsFacet() { EnsureAttribute<string>(HOSPITAL_NAME); EnsureAttribute<string>(PROFESSION_NAME); } public string HospitalName { get { return GetAttribute<string>(HOSPITAL_NAME); } set { SetAttribute(HOSPITAL_NAME, value); } } public string ProfessionName { get { return GetAttribute<string>(PROFESSION_NAME); } set { SetAttribute(PROFESSION_NAME, value); } } } } |
And the config patch file to register this custom facet
TC.Sitecore.Analytics.Model.config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <model> <elements> <element interface="Sitecore.TC.ExperienceProfile.ContactFacets.ICustomFieldsFacet, Sitecore.TC.ExperienceProfile" implementation="Sitecore.TC.ExperienceProfile.ContactFacets.CustomFieldsFacet, Sitecore.TC.ExperienceProfile" /> </elements> <entities> <contact> <facets> <facet name="CustomFields" contract="Sitecore.TC.ExperienceProfile.ContactFacets.ICustomFieldsFacet, Sitecore.TC.ExperienceProfile" /> </facets> </contact> </entities> </model> </sitecore> </configuration> |
To fillout the data in the custom facet I’ve setup a page to do this
How to setup the web API controller
First we need to register our custom route to our custom controller
InitializeRoutes.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System.Web.Http; using System.Web.Routing; using Sitecore.Pipelines; namespace Sitecore.TC.ExperienceProfile.CIntel.Endpoint.Plumbing { public class InitializeRoutes { public virtual void Process(PipelineArgs args) { RegisterRoutes(RouteTable.Routes, args); } protected virtual void RegisterRoutes(RouteCollection routes, PipelineArgs args) { routes.MapHttpRoute("tc_customcontact_customfields", "sitecore/api/ao/v1/contacts/{contactId}/customfields", (object)new { controller = "CustomContact", action = "GetCustomFields" }); } } } |
In our custom controller, we just return the custom facet that contains all the properties that we want to display in Experience Profile
CustomContactController.cs
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 |
using System; using System.Net; using System.Net.Http; using System.Web.Http; using Sitecore.Analytics.Tracking; using Sitecore.Cintel.ContactService; using Sitecore.Cintel.Endpoint.Plumbing; using Sitecore.Configuration; using Sitecore.Diagnostics; using Sitecore.TC.ExperienceProfile.ContactFacets; namespace Sitecore.TC.ExperienceProfile.CIntel.Endpoint { [AuthorizedReportingUserFilter] public class CustomContactController : ApiController { [HttpGet] public object GetCustomFields(Guid contactId) { try { var contactManager = GetContactManager(); var contact = contactManager.LoadContactReadOnly(contactId); if (contact == null) throw new ContactNotFoundException(); var customFacet = contact.GetFacet<ICustomFieldsFacet>(CustomFieldsFacet.FACET_NAME); return customFacet; } catch (ContactNotFoundException ex) { return Request.CreateResponse(HttpStatusCode.NotFound, ex.Message); } } private static ContactManager GetContactManager() { var contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager; Assert.IsNotNull(contactManager, "Could not create instance of ContactManager"); return contactManager; } } } |
Finally we add the InitializeRoutes class to the pipeline
TC.Sitecore.ExperienceProfile.config
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <initialize> <!-- Creates a set of http endpoints that expose our custom fields on a contact. --> <processor type="Sitecore.TC.ExperienceProfile.CIntel.Endpoint.Plumbing.InitializeRoutes, Sitecore.TC.ExperienceProfile" patch:after="*[last()]" /> </initialize> </pipelines> </sitecore> </configuration> |
How to setup a custom tab in Experience Profile
I will not go in detail on how I setup the content tree as the reference I’ve provided should give you an idea on how to set it up and I’ll be providing the source code at the end of this post.
This is the content tree in the core database that I’ve setup in order to display those 3 tabs
The first tab which is the Profession tab will be displaying the custom field from the custom contact facet that I’ve setup.
The rendering items
To display this information, after our Profession tab is loaded in the Experience Profile we would need to call our CustomContactController class to retrieve the data.
The code that’s responsible to do this is the profession.js that is set in the SubPageCode rendering control in the ProfessionPanel layout. This javascript file is executed when the ProfessionPanel item is loaded into the Profession tab
profession.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
define(["sitecore", "/-/speak/v1/experienceprofile/DataProviderHelper.js", "/-/speak/v1/experienceprofile/CintelUtl.js"], function (sc, providerHelper, cintelUtil) { var app = sc.Definitions.App.extend({ initialized: function () { var localUrl = "/customfields/"; providerHelper.setupHeaders([ { urlKey: localUrl } ]); var url = sc.Contact.baseUrl + localUrl; var $that = this; providerHelper.initProvider(this.ProfessionDataProvider, "", url, this.ProfessionTabMessageBar); providerHelper.getData(this.ProfessionDataProvider, $.proxy(function (jsondata) { cintelUtil.setText($that.ProfessionNameValue, jsondata.ProfessionName, true); cintelUtil.setText($that.HospitalNameValue, jsondata.HospitalName, true); })); } }); return app; }); |
The script basically calls an API method in the server which then gives back the response in a JSON format which we then assign to the controls defined in the ProfessionPanel item layout.
This is the full url it’s trying to access http://habitat.dev.local/sitecore/api/ao/v1/contacts/02b2e6c4-d38a-4e30-a18e-b4a31b63907d/customfields/
It’s passing the current contact id and a specific url ‘customfields’ which we route to our custom web API controller. It will then use the cintelUtil helper to bind the data to the text controls defined in our ProfessionPanel layout.
Source code is available in Github
I get the following error as soon as i click on the custom tab in experience profile:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: IsVisible is not defined;
Bindings value: visible: IsVisible, text: Text, attr: {title: Tooltip()}
at ko.bindingProvider.parseBindingsString (knockout-2.2.1.js:1936)
at ko.bindingProvider.getBindings (knockout-2.2.1.js:1916)
at ko.bindingProvider.result.getBindings (sitecore-1.0.2.js:147)
at ko.dependentObservable.disposeWhenNodeIsRemoved (knockout-2.2.1.js:2067)
at evaluateImmediate (knockout-2.2.1.js:1241)
at Object.ko.dependentObservable (knockout-2.2.1.js:1318)
at applyBindingsToNodeInternal (knockout-2.2.1.js:2051)
at applyBindingsToNodeAndDescendantsInternal (knockout-2.2.1.js:2019)
at Object.ko.applyBindings (knockout-2.2.1.js:2138)
at child.sync (sitecore-1.0.2.js:657
Please help what could be wrong.