Changing placeholders with Rules

Another question on StackOverflow sparked my interest.

The question is about whether there’s a way to specify what the placeholder of a templates control will be, based on a device.

I proposed the following solution:

“What you can probably do is use Sitecore’s Rules Engine, where you can create (or use a pre-existing) Condition to find out whether you are on a mobile device. Then you can also use the Action ‘Set placeholder to value’.”

This was just a theory, so I figured I’d actually get a working version up. I’m using the Jetstream demo website (which comes with the Mobile Device Detector module), so it’s using Sitecore 6.6, even though the question was regarding Sitecore 7. The basic idea translates, of course, and I’m sure there’s not too many differences (if any) regarding this anyway.

There’s a Global Rules folder located in /sitecore/system/settings/rules/conditional renderings, which will run at all times. This is called from the <insertRenderings> pipeline – from the Sitecore.Pipelines.InsertRenderings.Processors.EvaluateConditions processor (located in Sitecore.Kernel), to be exact.

globalrules

I’ll create a new Conditional Rendering Rule in that folder, and call it ‘Switch Placeholder’.

My condition will be: “Where the device is mobile”. My action will be: “Set placeholder to mobile_footer”. Of course, in my case I actually have a mobile device with layout and all, so all I’m trying to do is set the control in a different placeholder.

The condition comes with the Mobile Device Detector module. The action is in Sitecore out of the box.

 initialrule

Let’s give that a go (don’t forget to actually switch the device to mobile in Sitecore, otherwise you won’t see anything as ‘where the device is mobile’ won’t return true).

Before:

before

After:

after

Of course, there’s an issue – all renderings and sublayouts will now be moved to the mobile_footer. What one could do to prevent this is to check either which placeholder the rendering would originally target for example. Another option would be checking which rendering is actually targeted, which might be even better. A combination is possible as well of course (target only this specific rendering when it’s in this specific placeholder).

First I’ll be checking which placeholder is targeted.

To do this, I’ll have to create a new condition, as there is no existing (at least, not out-of-the-box) condition available to check this.

I’ll create my condition in /sitecore/system/settings/rules/conditional renderings/conditions/testapplication for now.

The Text field of my condition will be: where the placeholder [operatorid,StringOperator,,compares to] [Key,,,specific value]

My type field will contain the location of my class: TestApplication.SwitchPlaceholder.TargetPlaceholder, TestApplication

The actual class itself:

public class TargetPlaceholder<T> : StringOperatorCondition<T> where T:ConditionalRenderingsRuleContext
{
  public string Key { get; set; }

  protected override bool Execute(T ruleContext)
  {
    Assert.ArgumentNotNull(ruleContext, "ruleContext");
    var ph = ruleContext.Reference.Settings.Placeholder;

    if (!string.IsNullOrEmpty(ph) && !string.IsNullOrEmpty(Key))
    {
      return base.Compare(Key, ph);
    }

    return false;
  }
}

Please note that the name Key (highlighted in the example) must match the name of the variable defined in the Text field of the condition ([Key,,,specific value]).

If I then select the rule:

rulev2

Now it will only change the placeholder to be ‘mobile_footer’ for any rendering which targets the placeholder ‘mobile_main_content’.

Our last step is to do this only for specific renderings.

Again, I create a new condition in the same location.

The text this time looks a little more complex:

Rendering [operatorid,StringOperator,,compares to] [renderingid,Tree,root={EB2E4FFD-2761-4653-B052-26A64D385227},specific] rendering

But it basically translates to: A rendering needs to compare some renderingid. The ID I pass in is the ID for the /sitecore/layout item.

Fill in the type again. In my case: TestApplication.SwitchPlaceholder.TargetRendering, TestApplication

The code:

public class TargetRendering<T> : StringOperatorCondition<T> where T:ConditionalRenderingsRuleContext
{
  public string RenderingId { get; set; }

  protected override bool Execute(T ruleContext)
  {
    Assert.ArgumentNotNull(rulecontext, "ruleContext");
    var rendering = ruleContext.Reference.RenderingID.ToString();

    if (!string.IsNullOrEmpty(rendering) && !string.IsNullOrEmpty(RenderingId))
    {
      return base.Compare(RenderingId, rendering);
    }

    return false;
  }
}

Update the Rule will now be:

rulve3

Now it will only change the placeholder to mobile_footer for the BasicContentPage rendering with the placeholder mobile_main_content.

Advertisements

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