The idea that everything in your Sitecore content tree is "an item" is great once you understand how it works, but can be a bit confusing to people who are new to the software. It's a fairly common new-user mistake to set Layout Details on a Template item, rather than on the Standard Values item for the template. This leads to all sorts of "but why are my changes not visible on the website?" confusion.
Talking about this with a colleague a while back, I wondered if there was a way of trying to reduce the likelihood of this sort of mistake for new users. Here's a quick example of an idea I came up with:
If you look at the Core Database's definitions for the Content Editor ribbon then you'll find the information about what commands are bound to the Details and Reset buttons for Layout.
Under
/sitecore/content/Applications/Content Editor/Ribbons/Chunks/Layout/Details
you find:
And when you look up these two buttons' commands in the
commands.config
file you will find:
<command name="item:setlayoutdetails" type="Sitecore.Shell.Framework.Commands.SetLayoutDetails,Sitecore.Kernel" /> <command name="pagedesigner:reset" type="Sitecore.Shell.Applications.Layouts.PageDesigner.Commands.Reset,Sitecore.Client"/>
These bits of config map the UI commands to implementation classes which inherit from the base type
Sitecore.Shell.Framework.Commands.Command
and this includes a method for
QueryState()
which the UI framework calls (passing in the item being edited) so a command can say whether it should be visible or enabled.
Hence one way we can try and solve our problem is to inherit from the standard Sitecore command classes for these menu options and add in some extra logic. Since both commands need the same logic, we can factor that out into a separate class to avoid writing it twice. For the sake of blog-post simplicity, lets stick it in a static helper method. Something like this:
public static class SimplerLayoutHelper { public static CommandState QueryState(CommandContext context, Func<CommandContext, CommandState> baseMethod) { Assert.ArgumentNotNull(context, "context"); if (context.Items != null && context.Items.Length == 1) { Item itm = context.Items[0]; if (itm.Database.Name != "core") { Item ancestor = itm.Axes.SelectSingleItem("ancestor::*[@@name='templates' or @@name='content']"); if (ancestor == null) { return CommandState.Disabled; } if (ancestor.Name == "templates" && itm.Name != "__Standard Values") { return CommandState.Disabled; } } } return baseMethod(context); } }
The method takes the command context, and a function pointer to the base
QueryState()
method that we want to fall back to if we decide not to return a result. If we have a context item to work on, it checks we're not in the Core database, as we're not bothered about those items. Then it runs an ancestor query to decide if the item in question is a child of the Templates or Content parts of the tree (the two places where setting Layout Details makes sense).
If it's not in the right part of the tree then we return the "Disabled" state to grey out the button and prevent them being clicked. If it does have Templates as an ancestor then we make a further check to see if the name of the context item is "__Standard Values". If not, we return Disabled to prevent the layout details being set in the wrong part of the Template tree. And otherwise we just pass the context through to the original method to let Sitecore's code take care of the remaining scenarios.
The replacement command classes can then be put together very simply:
public class SimplerLayoutDetails : Sitecore.Shell.Framework.Commands.SetLayoutDetails { public override CommandState QueryState(CommandContext context) { return SimplerLayoutHelper.QueryState(context, base.QueryState); } } public class SimplerLayoutReset : Sitecore.Shell.Applications.Layouts.PageDesigner.Commands.Reset { public override CommandState QueryState(CommandContext context) { return SimplerLayoutHelper.QueryState(context, base.QueryState); } }
All we need to do is call our helper method and pass in the context and the base method from the original class.
These new command classes can be configured with a simple config patch:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <commands> <command patch:instead="*[@name='item:setlayoutdetails']" name="item:setlayoutdetails" type="Testing.SimplerLayoutDetails,Testing" /> <command patch:instead="*[@name='pagedesigner:reset']" name="pagedesigner:reset" type="Testing.SimplerLayoutReset,Testing"/> </commands> </sitecore> </configuration>
This just replaces the out-of-the-box Command implementations with the new classes above. With that in place, trying to set the Layout Details on the wrong bit of a template doesn't work any more:
On the Template item, the buttons are greyed out. But if you select the correct Standard Values item:
And that should help prevent confusion about where to make these settings...
↑ Back to top