The other day I was watching a presentation and learned something new. Out of the box, you can control insert options using the rules engine. Along that line of thinking, I decided to see if I could use the rules engine to customize what rendering variants show up on a component.
With some customizations, I was able to use the rules engine to add/remove available rendering variants. However, there is one caveat: Sitecore caches the rendering variants using a cache key made up of the current page ID and the rendering item (in /sitecore/layouts
) ID. If you change the rules that affect rendering variants, you’ll need to either recycle the app pool or do something that forces the rendering variant cache to clear.
Sitecore Configuration
Adding Tags
We’re going to start off by adding a taxonomy tag by right-clicking /sitecore/system/Settings/Rules/Definitions/Tags
and inserting a new Tag called “Rendering Variants”. This is important because it allows us to specify what rules will show up in the context.
Creating Rule Context
Then, we’re going to go to the content editor of Sitecore and find the rules root item, /sitecore/system/Settings/Rules
. From there, we’re going to right-click and insert a rules context folder called Rendering Variants. You should end up with something that looks like this:
Now we need to add some tags to the Default tag. For now, I’ve added “Item Hierarchy,” “Item Attributes,” and “Rendering Variants”:
Creating the Actions
Next, we’re going to go to the /sitecore/system/Settings/Rules/Definitions/Elements
folder and insert a new Element Folder called “Rendering Variants”:
In this newly created element folder, we want to insert two new actions:
- Add Rendering Variant
- Remove Rendering Variant
For the “Add Rendering Variant”, we’re going to set the Text to be:
add [renderingvariantid,Tree,root=/sitecore/content,specific] variant
For the “Remove Rendering Variant”, we’re going to set the Text to be:
remove [renderingvariantid,Tree,root=/sitecore/content,specific] variant
To break down the parts inside the bracket:
- variable name in code where we’ll store the value
- type of dialog to display
- options for the dialog
- text to show to the user
Lastly, we’re going to expand the Tags folder inside the Rendering Variants Element Folder and add “Rendering Variants” to the Default tag:
Code
Firstly, we need to setup our RuleContext which will store information needed for our rule:
public class FilterVariantsRuleContext : RuleContext
{
public IList<Item> Variants { get; set; }
}
Next, I want to write a base class for the rule action. When a rule condition is met, the rules engine executes rule actions. I created a BaseRenderingVariantOption that goes like this:
public abstract class BaseRenderingVariantOption<T> : RuleAction<T> where T : FilterVariantsRuleContext
{
private ID _renderingVariantId;
public ID RenderingVariantId
{
get
{
ID renderingVariantId = _renderingVariantId;
return renderingVariantId != (ID)null ? renderingVariantId : ID.Null;
}
set
{
Assert.ArgumentNotNull(value, nameof(value));
_renderingVariantId = value;
}
}
}
That “RenderingVariantId” is going to be populated from the “renderingvariantid” parameter from the brackets of the rule actions from the earlier section.
Next, we setup our AddRenderingVariant and RemoveRenderingVariant options:
public class AddRenderingVariant<T> : BaseRenderingVariantOption<T> where T : FilterVariantsRuleContext
{
public override void Apply(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, nameof(ruleContext));
Item contextItem = ruleContext.Item;
Item variant = contextItem?.Database.GetItem(RenderingVariantId);
if (variant == null)
return;
if(!ruleContext.Variants.Contains(variant, new ItemIdComparer()))
ruleContext.Variants.Add(variant);
}
}
public class RemoveRenderingVariant<T> : BaseRenderingVariantOption<T> where T : FilterVariantsRuleContext
{
public override void Apply(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, nameof(ruleContext));
Item contextItem = ruleContext.Item;
ID variantId = contextItem?.Database.GetItem(RenderingVariantId)?.ID;
if (variantId == (ID)null)
return;
Item variantToRemove = ruleContext.Variants.FirstOrDefault(v => v.ID == variantId);
if (variantToRemove != null)
ruleContext.Variants.Remove(variantToRemove);
}
}
That “RenderingVariantId” is going to be populated from the “renderingvariantid” parameter from the brackets of the rule actions from the earlier section.
Next, we setup our AddRenderingVariant and RemoveRenderingVariant options:
public class AddRenderingVariant<T> : BaseRenderingVariantOption<T> where T : FilterVariantsRuleContext
{
public override void Apply(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, nameof(ruleContext));
Item contextItem = ruleContext.Item;
Item variant = contextItem?.Database.GetItem(RenderingVariantId);
if (variant == null)
return;
if(!ruleContext.Variants.Contains(variant, new ItemIdComparer()))
ruleContext.Variants.Add(variant);
}
}
public class RemoveRenderingVariant<T> : BaseRenderingVariantOption<T> where T : FilterVariantsRuleContext
{
public override void Apply(T ruleContext)
{
Assert.ArgumentNotNull(ruleContext, nameof(ruleContext));
Item contextItem = ruleContext.Item;
ID variantId = contextItem?.Database.GetItem(RenderingVariantId)?.ID;
if (variantId == (ID)null)
return;
Item variantToRemove = ruleContext.Variants.FirstOrDefault(v => v.ID == variantId);
if (variantToRemove != null)
ruleContext.Variants.Remove(variantToRemove);
}
}
Finishing touches
Back into Sitecore, we’re going to go back to our Actions in /sitecore/system/Settings/Rules/Definitions/Elements/Rendering Variants
and fill in the Type field.
The Type field of Add Rendering Variant should be:
Example.AddRenderingVariant, Example
The Type field of Remove Rendering Variant should be:
Example.RemoveRenderingVariant, Example
Adding a Rule
To add a rule, we’re going to go back down to the Rendering Variants Rule Context Folder we created in the beginning, right-click on “Rules” and add a new rule. After the rule is created, there will be a field called “Rule” where you can edit the rule. If everything’s setup correctly, it should look like this:
NOTE: In case you skipped my introduction, Sitecore does caching around Rendering Variants so I’m not sure how practical this all is.