Decoupling through the Rules Engine

2

April 28, 2011 by Alistair Deneys

Recently I was designing a feature on a site that required dynamic behaviours. This kind of thing is right up the Sitecore rules engine’s alley so I made use of it in the design. But the use of the rules engine had larger benefits than simply meeting the requirements. More on that in just a second.

Before getting too far into this blog post I feel I should make mention of Sitecore 6.5 which landed in CTP last week and is now available on SDN for download. Apart from the usual bug fixes and performance enhancements the biggest changes are in OMS. In fact, it looks like OMS is getting a name change to DMS (Dynamic Marketing System), though being this is a CTP everything may be subject to change before RTM.

I’ve already had people asking if they should upgrade their solutions to Sitecore 6.5. At this very minute I would say no as the release is CTP. As soon as it hits RTM, go for it. Even if you’re not using DMS/OMS it’s always a good idea to keep as up-to-date as possible unless the most recent version has known issues that your solution can’t work around. Making smaller updates more regularly is generally easier than larger updates less frequently.

OK, so onto the rules engine.

What is the rules engine? The rules engine in Sitecore allows us to dynamically change things based on stuff that’s happening. Yes that’s a little vague, but that’s because the rules engine is used in so many part of Sitecore. But what I’ll be looking at specifically in this post is conditional rendering rules which allows for dynamically changing the behaviour of presentation components. There is a wide variety of conditions out of the box with Sitecore for determining when the rules should be applied. And we can even create our own conditions to cover scenarios that aren’t covered out of the box.

As I mentioned above, using the rules engine had greater benefits than simply meeting the requirements in that it allows decoupling the business logic from the implementation. Without the rules engine, every presentation object involved in the behaviour would need to have it’s code updated to implement the behaviour. This could be a lot of work and requires going back to the code for every small update. It also makes your code more complex and more difficult to understand.

Rules which you create for the rules engine consist of conditions and actions. Conditions convey something about the current request such as country of origin of the request, profile key comparisons or the logged on user. Actions cause something to happen when the conditions are met such as hide rendering, set rendering data source or set a rendering parameter.

To properly illustrate the power of the rules engine, let’s create a scenario we can work through. Let’s start simple and ensure our scenario can utilise out of the box rules engine components. Sitecore contains a lot of rules engine components out of the box and you should familiarise yourself with them by having a peek in /sitecore/system/settings/rules/conditional renderings (and the Sitecore 6.5 CTP which came out last week contains even more!). So for this simple scenario we want to set the data source of a rendering to a different item to read it’s data from when the user has viewed 5 or more pages on the site. In the real world, you might use this kind of thing to alter promotions displayed on a sidebar to the user to try and offer more enticing promotions the longer they stay on the site.

To configure this kind of personalisation we need to log into the Sitecore desktop, then open the marketing center application and expand the personalization item then select the rules item. This is where we’ll create our personalisation rule. Create a new Conditional Rendering Rule and name it “Different data source for longer visits” or something else descriptive. Now select the newly created rule and click the Edit Rule menu option on the rule field.

In the resulting rule set editor dialog, select the condition “where the page index compares to number”. This will cause that condition to be added to the current rule and highlight the text of the rule which is settable. Click the compares to link and change it to is greater than then click number and change it to 5. This is our complete condition. Now select the action we want to apply when the condition is true which is the “set data source to Item” action. Click the Item link to set the specific item we want to use as the data source. That is our completed rule.

image

Note the above dialog is from the Sitecore 6.5 CTP. If you’re using an earlier version of Sitecore your dialog will look different. A quick note about one of the differences with the 6.5 CTP. Notice the Add a new rule link at the bottom? This indicates we’ll be able to add multiple rules to a conditional rendering rule in Sitecore 6.5 (although the CTP this didn’t appear to work). This will save a lot of time when you have multiple rules to apply at once to a control.

Now we’ve defined our conditional rendering rule we can apply it to a rendering. Open the presentation definition for an item by navigating to the standard values of it’s template (cause we never define presentation directly on items), selecting the presentation tab then click the details button in the layout section. Click the control you want to apply the rule to (or click edit, then select the controls tab, then select the control and click the edit button) and in the personalization section tick the rule you created previously to have it active for this control. Click OK until all the dialog are closed, publish and you’re ready to go! Now when you visit more than 5 pages in a session the data source of the control you applied the rule to above will get changed.

Quick note about Sitecore 6.5 CTP differences (again). By default the personalization section of the control properties dialog is not shown. To display that section click the Sitecore orb at the top of the content editor and select Application Options. On the view tab select the Show the Personalization section checkbox. Sitecore 6.5 CTP also adds a new button to the Device Editor dialog (the dialog that lists all the controls and allows you to edit them) titled personalize that allows you to define personalisation rules directly on the control itself without having to first create a separate conditional rendering rule in the marketing centre. This will be great for one offs and basic personalisation as it’s limited in comparison to a conditional rendering rule. For more complex rules or rules you want to apply more than once to different components, then you’ll still want to create a conditional rendering rule item.

Now stop for a minute and assess what we’ve done. Without the rules engine we would have had to update the code of each control to first evaluate the condition, then secondly to adjust the behaviour of the control. By using the rules engine we’ve decoupled the business logic from the implementation. Now if we need to apply the same rule to another control it’s as simple as a little bit of configuration as opposed to the alternative if we weren’t using the rules engine, which would mean updating the code of those controls. And what if someone wanted a similar but different rule? With the rules engine we don’t have to update each control involved, instead we can just create the rendering rule and apply it.

But this wouldn’t be Sitecore unless we could extend it! And the rules engine is no exception. So let’s have a look at creating our own conditions and action.

Firstly, a custom action. The action we used above lets us change the data source to a specific item in the content tree, but I’d like to be able to set the data source by using a Sitecore query. We’ll need to start by creating our custom action as a new class which inherits Sitecore.Rules.Actions.RuleAction<T>.

using Sitecore.Rules.Actions; 
using Sitecore.Rules.ConditionalRenderings;

namespace RulesEngineDemo 
{
  public class QueryDataSourceAction<T> : RuleAction<T> 
    where T : ConditionalRenderingsRuleContext 
  {
    public string Query 
    {
      get;
      set;
    }

    public override void Apply(T ruleContext)
    { 
      var item = ruleContext.Item.Axes.SelectSingleItem(Query);
      if (item != null)
      {
        ruleContext.Reference.Settings.DataSource = item.ID.ToString();
      }
    }
  }
}

The Query property above will be automatically populated by the rules engine based on the user defined parameters to the action text which we’ll fill in when we register this class. Note that we’ve defined a constraint on the generic type T to ensure it’s a ConditionalRenderingsRuleContext which is used in the conditional rendering rules. The rules engine goes beyond just conditional rendering rules.

Next we need to register this class and create the action definition item. This is done inside Sitecore under the /sitecore/system/settings/rules/conditional renderings/actions. Create a new item based on the system/rules/action data template. Fill in the fully qualified type name of the class we created above into the type field. Now we need to define the action text.

We mark up the action text to tell the rules editor and engine how to present the settable parameters to the user and what kind they are. For our case we only need a single parameter to fill in the Query property of our class. This will be text as we’ll be expecting a Sitecore query. Fill in the text field with the following:

set data source using [Query,Text,,a query]

See how we define the directives for the rules engine inside the square brackets. There are 4 elements in the square brackets, each separated by a comma. The first element is the property of the action class this parameter will be set into. The next element describes the UI the user should use to enter the parameter. Take a peek at /sitecore/system/settings/rules/common/macros for a list of different UI elements you can use. The third element are parameters for the UI component. We don’t need any for our example so we leave it empty. The last element is the text to display in the rules editor when the parameter has not been set.

If you’ve done everything right you should now be able to apply this action as part of a rule. For testing, create a new conditional rendering rule with the condition of where true which means the action will always run (very handy for testing your actions). Now select the new action we created and specify the query. Something simple like setting the data source to the parent (..). Now go and adjust the personalization rules applied to a control in the presentation definition of an item, publish, then navigate to that page. Your query should be been run and the data source of the control changed.

We can also create our own conditions for the rules engine. Custom condition classes must inherit from one of the condition base classes, Sitecore.Rules.Conditions.OperatorCondition, Sitecore.Rules.Conditions.WhenCondition, or Sitecore.Rules.Conditions.StringOperatorCondition. For this demo let’s create a condition to detect when a query string key contains a certain value. This will be a string comparison operation so the class we need to inherit from will be Sitecore.Rules.Conditions.StringOperatorCondition. I also want to be able to set the query string key and value, so I’ll need to expose these as properties of the class.

using System.Web; 
using Sitecore.Rules;
using Sitecore.Rules.Conditions;

namespace RulesEngineDemo
{
  public class QueryStringCondition<T> :
    StringOperatorCondition<T> where T : RuleContext
  {
    public string Key
    {
      get;
      set;
    }

    public string Value
    {
      get;
      set;
    }

    protected override bool Execute(T ruleContext)
    {
      var qsValue = HttpContext.Current.Request.QueryString[Key];
      return base.Compare(qsValue ?? string.Empty,
        Value ?? string.Empty);
    }
  }
}

Notice in the Execute method above we use the base classes Compare method? This is because we don’t actually know what kind of comparison the user wants to make. This is a StringOperatorCondition which can use the StringOperator macro to allow the user to select how the text is compared (is equal, is equal case-insensitive, contains, not equal, etc).

Now we need to register this condition and define the condition text. This is similar to what we did above for actions, but we’ll put the condition definition under the conditions folder rather than the actions folder. In your condition definition item, enter the fully qualified name of the condition type above then enter the following as the text.

where the query string [Key,Text,,key] [operatorid,StringOperator,,compares to]
  [Value,Text,,value]

Being this is a condition, Sitecore will automatically make the where in the text above a link to negate the condition. The Key and Value parameters above should be straight forward. The operatorid parameter is provided by the base class of our custom condition type.

Now we can create a new conditional rendering rule using this condition to detect query string values. Go ahead and give it a try.

I must say I’m quite excited about the rules engine as I’m now discovering how to apply it in real projects and not just those contrived marketing examples we’ve all seen where the data source of a control is changed. The rules engine has so much more power than that, and it allows for loosely coupled logic and behaviours (have I mentioned that point enough yet?).

For more information on the rules engine check out the The Rules Engine Cookbook on the SDN.

Advertisements

2 thoughts on “Decoupling through the Rules Engine

  1. Alexey Rusakov says:

    Great post.

    There have been _a lot_ of interest on rule engine during Dreamcore NA. Its not easy to see at first, but this is really powerful.

    One of the surprises is that we had rule engine in Sitecore since version 6, though upgrade is still highly encouraged for lots of improvements, both UI and core, and usages throughout the CMS.

  2. Ivan Huang says:

    It was great fun to use rules engine, lots of the potential and add values to the website.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

The views expressed on this blog are solely my own and do not necessarily reflect the views of my employer.
%d bloggers like this: