Making the Layout Details button a bit more user friendly

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.

Layout Details Mistake

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:

SetLayoutDetails

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:

Wrong Item

On the Template item, the buttons are greyed out. But if you select the correct Standard Values item:

Right Item

And that should help prevent confusion about where to make these settings…

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