Sunday, March 16, 2014

Checking with your own data to drive a Sitecore personalization condition

Why should I care about this?

Sitecore comes with an impressive list of prebuilt conditions to drive personalization--from referring URLs to the fact that a visitor comes to the site as a campaign respondent to GeoIP attributes of country, zip code and more.  In many discussions I have on the topic of personalization, the conversation inevitably leads to "yeah, but I want to use THIS thing."

To generalize, the THIS thing is some existing system or service, external to Sitecore, which holds a key piece of information.  Since this external system should remain the system of record for that piece of information, it makes sense for Sitecore to simply leverage the data where it sits, rather than enforcing that it move directly into the Sitecore content tree.  For those of you that have worked with Sitecore, you know it’s built for this.  Rather than enforce a migration of data to the content repository, a well developed Sitecore strategy continues to take advantage of data and application functionality wherever it is best served from.  Personalization conditions are no different.

Below I will work through a common scenario.  We want to develop a condition that queries an external database and determines whether a product is available before showing the promotion our marketing team has created for it.  With the condition in place, it will be easy for us to build a rule that does something when the condition is true.  This is a classic “When” condition, and we can think of all kinds of rules that might be appropriate based on the true / false value returned.

Who in my organization creates these conditions?

As an aside, many of my conversations lately have involved questions like "who sets up personalization in Sitecore?”.  A very fair question.  I enjoy the discussion because it really highlights that there is a transition going on here.  Sure, every piece of conditional logic could be developed separately (it is software, and everything is possible), but the question is sparked because this capability is now in the hands of many more people in the organization.  It’s not JUST for developers anymore.  While developers are certainly creating the great methods that evaluate conditions and implement rule-driven functionality, others in the organization (you get to decide who) are mapping the business processes and visualizing the results.  These two sides are equally important, and those organizations who find the people with the right combination of skills will certainly benefit.

Creating the custom condition

Back to it.  1st step is to create an item to define the condition.  Who does this?  Well, in most cases this one’s for the developer.  The reason is that we’re coding brand new logic here.  If you’re talking about the rich set of standard Sitecore personalization conditions (Geolocation info, DNS info, authenticated user profile attributes, etc), then there’s no work to do here.  These Conditions are already defined and available to you as you build a rule to evaluate that particular Condition.  More on that later.  The screenshot below shows the beginning of the scrollable list of prebuilt Conditions:




In standard Sitecore fashion, all prebuilt Sitecore functionality is built and referenced in the same way you’ll build your specialized or customized functionality.  If you look in the Content Editor at:

/sitecore/system/Settings/Rules/Definitions/Elements/GeoIP/Area Code

you’ll see the definition (the Item in the tree) that represents the condition.  Even though this is a standard Sitecore Condition, we are still able to see where this code is called from in the core application (in this case, the Sitecore.Analytics assembly contains the code for this condition):

The Default "Area Code" condition in Sitecore


You’ll notice that the AreaCodeCondition class in the Sitecore.Analytics assembly is referenced.  You’ll also see that the Text field allows you to define placeholders where a content author can enter additional parameters to the condition evaluation (this Text appears in the Rules Editor when adjusting the specific condition for a personalization rule).

So for our purposes today, it’s going to be even easier.  Rather that doing a compare of values between Analytics data and content author entered parameters, we’re simply going to check the “trueness” of a condition.  Does the product have any availability?  Based on the result, we can then decide what to do about it.

A strategy for setting up custom conditions and rules

In regards to the question again about "who does what?", this is a typical thought process around setting up these types of conditional rules:
  • A cross-functional team discusses the Conditions that need to be evaluated.  Many will be available in Sitecore already, some will have to do with your business-specific data that Sitecore couldn't possibly know about natively.
  • A cross-functional team discusses the Rules, the things that should HAPPEN when a Condition is evaluated.  Again many will exist in Sitecore already (easily hiding or showing a control, having a control display a different piece of content, moving a control to a different area of the page, initiating a specific Email Campaign, etc.) and some will do fun and exciting things outside of the Sitecore application domain and context.
  • A cross-functional team discusses where and when these rules are appropriate, and which groups should have the ability to adjust the rule set.
  • A cross-functional team ensures that both sides of the brain communicate, and that the development efforts and the business process mapping stay on the same (albeit flexibly winding) trail.  
The point here isn’t that you need a huge cross-functional team.  The point is that if you’re a team of one (or 2, or 10, or 100), you need to think cross-functionally.  Nothing new here.

The three steps to implement the custom condition and rule

OK, so again for this example we simply want to rely on an external system to determine whether a condition exists.  We'll use the freely available AdventureWorks database from Microsoft.  The basic idea will be to:
  1. Add a new condition item to Sitecore (so that someone in our organization can choose the condition as part of their personalization rules for a control).
  2. Write a custom condition that looks to AdventureWorks to find out if there is product inventory for a certain product.
  3. Add our new condition to control on the Launch Sitecore home page so we show a different spot if the product is available.

Step #1

We need to add the new item to the content tree to reference our custom condition.  I put the item at

/sitecore/system/Settings/Rules/Definitions/Elements/Fields/

...so it would show up near the top of our list when we apply it to a control.

Our new custom condition

Since we're making the condition check very simple (we're going to always look for product availability for a product with an ID of 1), we don't need to set up the Text field with any macro replacement possibilities.  In the real world, we'd want to either allow an author to choose a specific product using this technique, or we could think about making that decision based on which page the control is on (i.e., a product page dedicated to a particular product could also pass that product ID directly to our inventory check routine).

Step #2

Now we need to write some code for the actual condition check.  The main Execute method is shown below.  Note that I used the freely available Entity Data Model Wizard within Visual Studio to set up an easy LINQ query to the AdventureWorks database.

        protected bool Execute(T ruleContext)
        {
            var numUnits = 0;
            try
            {
              using (AdventureWorks2012Entities context = new AdventureWorks2012Entities())

                numUnits = (from m in context.ProductInventories
                                where m.ProductID == 1
                                select m.Quantity).FirstOrDefault();

                return numUnits > 0;
            }
            Catch

            { return false; }


        }


Now, when a personalization rule includes this condition check, we'll simply go to the AdventureWorks database and check for availability of ProductID 1.  Since the conditional check is basically a true/false result, we'll return whether or not the product availability for that product is greater than 1.  With my new condition added to the content tree (described above), this is where we can see it (in the Fields section).  You can organize your custom conditions anywhere that makes sense.

Our new condition shows up in the list for a business user to choose


Step #3

Now we can add this to the simple spot controls on the Launch Sitecore home page, showing the Einstein spot if this condition is true.  Below shows the Page Editor view where I can add a New Condition to my control, calling out my check to AdventureWorks, and the "Step 1" content item the control will use as its datasource if my inventory check returns True:

Our new condition is now part of the Rule Set for our Home Page control

Hopefully this sheds some light on how achievable it is to consider external data personalization sources in Sitecore.  Taking the next steps and creating bridges to product catalogs, existing systems of record and web services can really open up your personalization options without changing repositories and services that already do their jobs incredibly well.