Tuesday, June 3, 2014

Custom rendering conditions for Sitecore presentation components

Conditional rendering rules in Sitecore are used to personalize specific components on a page based on visitor variables. It is fairly easy to extend the Rules Engine and create custom conditions to satisfy specific business needs and requirements. There are a lot of posts out there on how to create custom rules and conditions in Sitecore, so I'm going to try and keep this concise. There are three steps, The WHAT, The HOW, then Tying it all together. Enjoy!

1. The WHAT
The first step to creating a custom condition is defining what exactly it would do and choosing which base Sitecore condition type to inherit from. The below chart illustrates the inheritance between the base condition types in the Sitecore.Rules.Conditions namespace:



In this example, we are going to allow the user to select a specific value that represents a Sitecore item on the website UI, and personalize the content we render based on that value. Since this would be similar to the ItemIdCondition, which inherits from StringOperatorCondition, we will go with the same base.

2. The HOW
The below diagram illustrates the basic logic flow between our website (the almighty UI), our Sitecore rules engine component (the custom condition) and a custom value provider (a.k.a business domain code).

This separation of concerns allows us to contain all logic regarding the storage and retrieval of our value in its dedicated ValueProvider. Whether this value comes from a database, is stored in a cookie, or is a session variable is thus irrelevant to the condition and the evaluation of the condition. It also becomes irrelevant to the website UI, which will only delegate the responsibility of getting and setting the value to the same provider as well.

Here's an example of a condition class that uses a provider to retrieve the current context value. In an ideal scenario, instead of instantiated here, the provider implementation would be injected through a dependency injection container (pick your weapon carefully).

    public class SelectedValueCondition<T> : StringOperatorCondition<T> where T : RuleContext
    {
        private static ISomeValueProvider _someValueProvider;
        public static ISomeValueProvider MyAwesomeProviderInstance
        {
            get
            {
                if (_someValueProvider == null)
                {
                    _someValueProvider = new AwesomeProviderImplementation();
                }

                return _someValueProvider;
            }
        }

        private ID _itemid;
        public ID ItemID
        {
            get
            {
                return _itemid;
            }
            set
            {
                Assert.ArgumentNotNull(value, "item id");
                _itemid = value;
            }
        }

        public SelectedValueCondition()
        {
            _itemid = ID.Null;
        }

        public SelectedValueCondition(ID itemId)
        {
            Assert.ArgumentNotNull(itemId, "item id");
            _itemid = itemId;
        }
 
        protected override bool Execute(T ruleContext)
        {
            Assert.ArgumentNotNull(ruleContext, "ruleContext");

            // let's say our ValueModel class represents a domain entity that has a property named 'ID'
            ValueModel model = MyAwesomeProviderInstance.GetValue();

            ID modelId = !string.IsNullOrEmpty(model.ID) ? new ID(model.ID) : ID.Null;
            ID itemId = ItemID;

            return !modelId.IsNull && !itemId.IsNull && Compare(modelId.ToString(), _itemid.ToString());
        }
    }

3. Tying it all together
Adding our condition to Sitecore is straight-forward. An important thing to remember is to set the path of the items, from which a content editor should be able to choose from:

And that is it! Now this rule is ready to be applied to any presentation component that uses a data source.