Conditional Placeholder Settings

I’ve always considered the rules engine to be a pretty cool part of Sitecore. In this post, I’ll talk about using the Sitecore rules engine in order to affect Sitecore placeholder settings in the page editor (or experience editor as it’s soon to be called).  I’ve also put the code for this module (for want of a better word) as it stands in a GitHub repository.

A lot of people are familiar with the rules engine in the context of the personalisation functionality but don’t realise that, out of the box, it can be used for other things too. A few of the most useful (in my opinion) examples are: insert options, search index boosting, and tacking things on to various item events.  I’m not going to spend too much time on the basics of the rules engine, for that John West has a good list of posts, instead I’m just going to write a bit about how I went about writing the module.

I was working on a project earlier this year that made a great deal of use of the page editor and placeholder settings, but toward the end of the project maintaining the placeholder settings for all the different placeholders became a bit of a hassle with having to keep track of which renderings were available where. Due to time constraints we never actually implemented this despite it being a particularly neat solution, but I stumbled across the idea in my notepad and thought it would be a nice little thing to hack on.

I figured that there are two main actions that administrators might want to perform with these rules: making a rendering available in a placeholder and making a rendering unavailable in a placeholder. They may also want to be able to stop editors from modifying placeholders, for completeness.  In terms of the particular conditions that are needed, the main thing that is not already present is the ability to make decisions based on the placeholder key, so I added that to my to-do list.  Conditions dealing with security, templates and such already exist in default Sitecore installs, I just had to make sure they can be used.

Having previously dabbled with dynamic placeholders, I figured that the best place to extend Sitecore’s functionality is the getPlaceholderRenderings pipeline.  This pipeline is ultimately responsible for determining which renderings are allowed to be placed in any given placeholder.  Coincidentally, it also builds up the URL for the ‘add rendering’ dialog.

Since the new actions and conditions are intended to modify placeholder settings, I felt that the best choice was to create my own specialisation of the default RuleContext class: PlaceholderRuleContext.  Its intention is to provide all the necessary information for custom actions and conditions to get information about placeholders and modify them.  I opted against direct modification of the main allowed renderings list, as I felt it could give rise to unexpected behaviour when rules execute in a different order than was anticipated.

Getting the rule editor and running was actually much more straight-forward on 7.1 than when I had a go on 7.0, due to the changes in the structure of the rules folder.  I pretty much just had to do the following:

  1. Create a new element folder under /sitecore/system/Settings/Rules/Definitions/Elements
  2. Tag the folder
  3. Create some custom actions and conditions (mapping them back to a C# type)
  4. Set up a new rule context folder under /sitecore/system/Settings/Rule
  5. Link that to the right tags
  6. Start creating rules.

I was initially surprised that the getPlaceholderRenderings pipeline executes twice for a placeholder: once when the page editor loads, and once when the ‘add rendering’ dialog is opened.  I had originally expected (for no real reason) the dialog to get passed the allowed renderings by the page editor, but that is not the case.  Given that the dialog call is always a shell call, I had to do a bit of “massaging” of the Sitecore context (item, device, etc.) in order to ensure that some of the vanilla conditions function as expected.

I got the basics up and working pretty quickly.  Originally I started with an ApplyRuleResult processor, which kept on getting more complex as I found edge cases that I had not considered.  For example: if I had started off with an editable placeholder settings item with no allowed renderings, then I should allow anything to be added; but if I had started off with a list of allowed renderings from which rules had removed everything, then I should not allow anything to be added.  I ended up splitting that into several processors, although I’m still not entirely happy with it.

The next issue I ran into was that the conditions are, as previously mentioned, only processed when the page editor loads and when the dialog loads.  So, if something changes on the page (I don’t know, maybe for some odd reason a placeholder should only be editable every third minute), then the page editor can be presenting a placeholder that the editor cannot in fact edit.  This is somewhat mitigated, because the dialog will at least show the correct set of renderings; but it isn’t a great editor experience.

I also quickly found out that the page editor was conspiring against me.  Cleverly, it does not require users to save when they add new renderings to the page.  So, you can have several renderings added to a placeholder before the placeholder rules (even inside the dialog) before the user saves and the placeholder rules become aware of them.  This was a bit annoying, as one thing I wanted to do was to have a rule saying that editors could only add N renderings to a placeholder, and this totally blew that out of the water.  I’m fairly sure that there’ll be a way I can access this, but at time of writing I’m considering it a “known issue”.

Those issues aside, from the testing I did, it actually all seems to work pretty well.  As I said at the start, I’ve uploaded all the code to a GitHub repository so if you’re interested in doing something similar, go take a look.