Categories
Bloggers
Blogs RSS feed

Content Personalization Using Custom Criteria

by Bilyana Ivanova

Delivering personalized content to specific target audiences is a powerful way to attract website visitors and promote the right content to the right people, at the right time. This is how you can display products of regional interest to localized target groups, maybe display time-limited promotions to various user segments or highlight search-keyword related content for people that reached your website using these in a search engine. These are very typical scenarios that you expect to be covered by any system, capable of delivering content personalization. However,  whenever we want to deliver personalized content for user segments, defined by some criteria that are not so trivial – for example customer ranking in your CRM or membership in groups, we hit a roadblock, as usually we are expected to use the built-in functionality only. Or at least that’s the case if you’re not using Sitefinity.

With the personalization feature Sitefinity brings the ability to deliver personalized content, targeted to specific audiences (user segments) based on certain criteria: location, profile fields, previous purchases, etc. You can build complex logical expressions in order to define precisely the audience you want to deliver your message to. And while the built-in criteria options will cover many scenarios, in case you need something custom, you still have that option to add a new criterion.

In my previous blog post I demonstrated how you can Integrate Sitefinity with SalesForce CRM. Now, in order to demonstrate implementing custom criteria, we will pull the rating of a lead in our CRM system (SalesForce). Our goal could be to show hot leads a promo code to give them that final push they need, before they place an order.

In order to add a custom criterion you need to:

  1. Create a dialog/editor for that criterion, where users will set its expected value. Create another editor for the personalization console (the “view website as” page), where you can test how the site will come up if the criterion is met and if it is not
  2. Create a resource file for the editors
  3. Create an evaluator, which will determine whether the  currently logged user matches the criterion or not
  4. Create the criterion
  5. Finally, use that criterion within a user segment

Now, let's look at each step into more detail:

Step 1

We need the editor for setting the criterion. In our case we need a simple drop-down with the possible lead rating values: hot, warm, cold.

 Set the Build Action for the *.ascx file to Embedded Resource.

<label for="rating" class="sfTxtLbl">{$CustomPersonalizationResources, LeadRating$}</label>
<select id="rating">
    <option value="1">{$CustomPersonalizationResources, Hot$}</option>
    <option value="2">{$CustomPersonalizationResources, Warm$}</option>
    <option value="3">{$CustomPersonalizationResources, Cold$}</option>
</select>
<script type="text/javascript">
    $(document).ready(function(){
        if (document.URL.toString().indexOf("personalization-console",0)!=-1) {
            document.getElementById("ratingLabel").style.display = "none";
        }else {
            document.getElementById("ratingLabel").style.display = "block";
        }
      })
 
    function CriterionEditor() {
    }
 
    CriterionEditor.prototype = {
     //Used as a label for the criterion when viewing the user segment
        getCriterionTitle: function () {
            return "SalesForce lead status"
        },
        //The descriptive value for the criterion
     getCriterionDisplayValue: function () {
            return rating.options[rating.selectedIndex].text;
        },
      
     //Persists the input from the user as a value for the criterion
        getCriterionValue: function () {
            debugger;
            return rating.options[rating.selectedIndex].value;
        },
 
     //When the editor opens, sets the previously persisted value
     //as initial value for the criterion
        setCriterionValue: function (val) {
            rating.value = val;
        },
 
        errorMessage: "",
 
        isValid: function () {
            return true;
        }
    };
</script>

We need another editor for the personalization console where you can define a custom set of characteristics. We can also reuse the editor we already have and simply hide the label and leave visible only the input fields.

Step 2

We should create a localization resource for the new editors. It should inherit from Telerik.Sitefinity.Localization.Resource . We need an entry for the criterion’s name and for  the labels and values appearing on the editors.   

[ObjectInfo(typeof(CustomPersonalizationResources), Title = "CustomPersonalizationResourcesTitle", Description = "CustomPersonalizationResourcesDescription")]
    public class CustomPersonalizationResources : Resource
    {
        public CustomPersonalizationResources()
        {
        }
 
        public CustomPersonalizationResources(ResourceDataProvider dataProvider)
            : base(dataProvider)
        {
        }
 
        [ResourceEntry("CustomPersonalizationResourcesTitle",
    Value = "Personalization Custom",
    Description = "Custom Personalization",
    LastModified = "2013/06/30")]
        public string ProductsResourcesTitle
        {
            get { return this["CustomPersonalizationResourcesTitle"]; }
        }
 
        [ResourceEntry("CustomPersonalizationResourcesDescription",
    Value = "Contains localizable resources for custom personalization.",
    Description = "Contains localizable resources for custom personalization.",
    LastModified = "2013/06/30")]
        public string ProductsResourcesDescription
        {
            get { return this["CustomPersonalizationResourcesDescription"]; }
        }
 
        [ResourceEntry("LeadRating",
            Value = "Lead Rating",
            Description = "The rating of the SalesForce lead",
            LastModified = "2013/06/30")]
        public string LeadRating
        {
            get { return this["LeadRating"]; }
     }
 
    
    }

We can then reference those elements on the editors like this:

<label class="sfTxtLbl" for="profileTypes">
            {$<LocalizationResource>, <ResourceEntry>$}
</label>


Step 3

The next step is to define the business logic for determining if the visitor of the website is a match. For that we need an Evaluator, which implements the ICriterionEvaluator interface, i.e. the IsMatch method. As an input we get the value of the criteria set for the user segment and also the context of the request.

public class SalesForceRatingEvaluator : ICriterionEvaluator
{
    public bool IsMatch(string settings, IPersonalizationTestContext testContext)
    {
 
        string rating;
 
        //Get the current user
        //rating = Request the rating of the user from SalesForce
        //if(rating.Equals(settings)){
        //  return true;
        //}else{
        //  return false;
        //}
    }
}  

Step 4

The final step is to register all those elements when the application starts. For that, we add an event handler for the Bootstrapper.Initialized event in the Global.asax file. Here we need to:

  1. Register the resource file
  2. Add a virtual path for the embedded resources (the editors)
  3. Create the criteria
  4. Register the evaluator for the criteria

public class Global : System.Web.HttpApplication
{
 
    protected void Application_Start(object sender, EventArgs e)
    {
        Bootstrapper.Initialized += Bootstrapper_Initialized;
    }
 
    private readonly string criteriaName = "LeadRating";
    private readonly string virtualPath = "~/SFCustomPersonalization/";
 
    void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
    {
        //Register the resource file
        Res.RegisterResource<CustomPersonalizationResources>();
 
        //Add a virtual path for the embedded resources (the editors)
        var virtualPathConfig = Config.Get<VirtualPathSettingsConfig>();
        if (!virtualPathConfig.VirtualPaths.Contains(virtualPath + "*"))
        {
            var pathConfig = new VirtualPathElement(virtualPathConfig.VirtualPaths)
            {
                VirtualPath = virtualPath + "*",
                ResolverName = "EmbeddedResourceResolver",
                ResourceLocation = "SitefinityWebApp"
            };
            virtualPathConfig.VirtualPaths.Add(pathConfig);
 
        }
 
        //Create the criteria
        var personalizationConfig = Config.Get<PersonalizationConfig>();
        if (!personalizationConfig.Criteria.Contains(criteriaName))
        {
 
 
            CriterionElement rolesCriterion = new CriterionElement(personalizationConfig.Criteria)
            {
                Name = criteriaName,
                Title = criteriaName,
                ResourceClassId = typeof(CustomPersonalizationResources).Name,
                CriterionEditorUrl = "SitefinityWebApp.SalesForce.Personalization.SalesForceRatingEditor.ascx",
                ConsoleCriterionEditorUrl = "SitefinityWebApp.SalesForce.Personalization.SalesForceRatingEditor.ascx",
                CriterionVirtualPathPrefix = virtualPath
            };
            personalizationConfig.Criteria.Add(rolesCriterion);
        }
 
        //Register the evaluator for the criteria
        ObjectFactory.Container.RegisterType(
            typeof(ICriterionEvaluator),
            typeof(SalesForceRatingEvaluator),
            criteriaName,
            new ContainerControlledLifetimeManager(),
            new InjectionConstructor());
    }
}

 

If you’re looking for more info on the personalization feature in Sitefinity, we’ve prepared a whitepaper that explains its usage and benefits. You can review it here.

3 Comments

  1. 1 Andreicho 20 Aug
    It would be very helpful if author could include screenshots and a video
  2. 2 Bart 22 Oct
    I'm wondering how you structured these changes inside the solution.  Is this a custom control or did you make a folder under the SitefinityWebApp project and just put all the files used in it?  I also think these are just code snippets for key parts, the example with complete files would be great.
  3. 3 Iva Kostova 14 Jan
    This is a test comment. Please, ignore it!

Comment

  1. RadEditor - HTML WYSIWYG Editor. MS Word-like content editing experience thanks to a rich set of formatting tools, dropdowns, dialogs, system modules and built-in spell-check.
    RadEditor's components - toolbar, content area, modes and modules
       
    Toolbar's wrapper 
     
    Content area wrapper
    RadEditor's bottom area: Design, Html and Preview modes, Statistics module and resize handle.
    It contains RadEditor's Modes/views (HTML, Design and Preview), Statistics and Resizer
    Editor Mode buttonsStatistics moduleEditor resizer
      
    RadEditor's Modules - special tools used to provide extra information such as Tag Inspector, Real Time HTML Viewer, Tag Properties and other.