Personalization

Previewing Personalization Rules by Derek Hunziker

preview-personalization.png

It’s a fairly common practice for Sitecore content authors to stage content and provide URLs for stakeholders to review. With preview publishing targets, this process is a breeze to get up and running. However, when there are personalization rules involved, providing preview URLs becomes a bit more challenging. How do we share all of that personalized goodness with others, before it goes live?

Often times, reviewers are external to your organization or may not have access to the Sitecore back-end. Your stakeholders may also be very busy and don’t have time to login to Sitecore, locate pages, launch Experience Editor, and cycle through every possible personalization rule. Furthermore, it can be very difficult (and sometimes impossible) to trigger personalization rules by matching the actual conditions as if you’re a real website visitor. These challenges are compounded when your list of personalization rules begins to multiply.

The technique described below aims to address this challenge. The idea is pretty simple: we will use a query string to trigger a personalization rule, based on it’s name in Sitecore.

When rules are added in Sitecore, we are given the opportunity to apply a name for each one, as shown below:

rule-name.png

From this alone, with no other setup or configuration, we can visit the page with ?rule=Email Marketing Channel appended to the URL, and the matching rule will be triggered, regardless of how complex the condition is or where it sits in terms or priority ordering.

Here’s how it works in a nutshell:

  1. We make our own Mvc.Analytics.Pipelines.Response.CustomizeRendering.Personalize pipeline.

  2. Override the RunRules() method.

  3. Retrieve the query string from the request.

  4. Determine if the query string matches any of the rules (by name).

  5. If a match is made, we create a NEW rule with the same action(s), set it to use a TrueCondition (a.k.a always true), and temporarily inject it at run-time. This guarantees that it will be triggered without any other rules interfering.

We begin by overriding Sitecore’s default Personalize pipeline. This pipeline is responsible for running each personalization rule until the first matching condition is found. Here, we are using it to manipulate the RuleList before it is executed. This is also a good time and place to apply a feature switch/toggle, or limit this functionality to pre-production environments only, should you so choose.


protected override void RunRules(RuleList<ConditionalRenderingsRuleContext> rules, ConditionalRenderingsRuleContext context)
{
  Rule<ConditionalRenderingsRuleContext> injectedRule = GetInjectedQueryStringRule(rules);

  if (injectedRule != null)
  {
    var newRules = new RuleList<ConditionalRenderingsRuleContext>() { Name = rules.Name };
    newRules.Add(injectedRule);
    rules = newRules;
  }
  
  base.RunRules(rules, context);
}

The method “GetInjectedQueryStringRule()” above is what’s responsible for returning a new rule (based on the one that matches the query sting). If a rule is returned, we build a new RuleList.

NOTE: The RuleList class does not appear to provide a straightforward way to add a rule to the beginning of the list. Therefore, a new RuleList is created with the injected rule alone.

Next up is the method that builds out the injected rule. It accepts the component’s default RuleList and uses is to make a match on the query string, then creates a new rule:


private Rule<ConditionalRenderingsRuleContext> GetInjectedQueryStringRule(RuleList<ConditionalRenderingsRuleContext> rules)
{
  string ruleQuery = WebUtil.GetQueryString("rule");

  if (!string.IsNullOrEmpty(ruleQuery))
  {
    foreach (var rule in rules.Rules)
    {
      if (ruleQuery.Equals(rule.Name, StringComparison.OrdinalIgnoreCase))
      {
        TrueCondition<ConditionalRenderingsRuleContext> trueCondition = new TrueCondition<ConditionalRenderingsRuleContext>();
        return new Rule<ConditionalRenderingsRuleContext>(trueCondition, rule.Actions)
        {
          Name = "Injected Rule",
          UniqueId = ID.NewID
        };
      }
    }
  }

  return null;
}

With the improved pipeline in place, we can now trigger any personalization rule (including the Default one) by simply appending a query string with whatever rule name we want. For example:

  • http://example.com/page-with-personalization?rule=Default

  • http://example.com/page-with-personalization?rule=WhatMarketersSee

  • http://example.com/page-with-personalization?rule=WhatDevelopersSee

  • http://example.com/page-with-personalization?rule=WhatCSuiteSee

Happy previewing!