This blog has moved to http://jermdavis.wordpress.com/

Monday 17 February 2014

Confirming Sitecore Commands

Be honest people: how many of you have wasted a few hours of your life by accidentally clicking “Revert Database” when you meant to click “Update Database” after a long evening at the development coal face? I certainly have. And if you’re like me and tend to serialise only the things you’ve changed in your project, then that means you generally end up with a dead instance of Sitecore.

Once is enough for this sort of mistake, and it lead me to thinking about how you can customise the Sitecore UI to prevent these issues in a generic way. What I decided to try was some code to allow you to configure a confirmation dialog before the action of any command on the ribbon. And it turns out it’s not too hard to do.
The starting point for anything triggered by clicking a button in the ribbon is a class which inherits from Sitecore.Shell.Framework.Commands.Command, and they require an Execute() method:

namespace Blog.Examples
{
    public class ConfirmCommand : Command
    {
        public override void Execute(CommandContext context)
        {
        }
    }
}

Whenever you want to make use of a class derived from Command in your ribbon configuration, you need to register it in the Sitecore configuration. And that means a quick configuration patch. Each entry in the commands collection in configuration needs two bits of data. The “type” attribute is a normal .Net type descriptor for the class you’ve implemented, and the “name” attribute is the textual name you will use later when you specify what gets called when a ribbon button is clicked. So we can use example:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="extensions:confirm" 
 type="Blog.Examples.ConfirmCommand,Blog.Examples" />
    </commands>
  </sitecore>
</configuration>

With that done, we need to make the command present some UI. Having done a bit of spelunking through the commands that ship with Sitecore, they share a common pattern where the command launches a pipeline in order to allow the UI to display UI and then process its results:

public class ConfirmCommand : Command
{
    public override void Execute(CommandContext context)
    {
        Sitecore.Context.ClientPage.Start(this, "Confirm", 
context.Parameters);
    }

    protected void Confirm(ClientPipelineArgs args)
    {
    }
}

The next thing to consider is how the code will find out what it is supposed to be confirming. Looking at some of the commands that ship with Sitecore, they get parameters passed from their item in the ribbon in the definition of their click event:

image

Examining the code for these commands shows that the parameters inside the brackets are parsed into the Parameters collection of the commands context object as a name/value collection. A quick test shows that this means you can just wrap the command you want to confirm as a parameter of the confirm command itself. So, thinking this through the confirm command will also need to know what text to put in the confirmation dialog, so we need two parameters:

extensions:confirm(title=revert the database,
         cmd=itemsync:loaddatabase(revert=1))

The “title” parameter optionally provides the text to append to the dialog message, and “cmd” provides the command to be executed. And the code to process this looks like this: (Error handling and validation omitted for clarity)

protected void Run(ClientPipelineArgs args)
{
    string msg;
    if (!string.IsNullOrWhiteSpace(args.Parameters["title"]))
    {
        msg = "Are you sure you want to " + 
args.Parameters["title"] + "?";
    }
    else
    {
        msg = "Are you sure you want to do that?";
    }

    SheerResponse.Confirm(msg);
    args.WaitForPostBack();
}

The call to args.WaitForPostBack() signals to the internals of Sitecore that this code wants to know the response from the dialog box that we’ve displayed. What happens as a result of that is when the user makes their click our code gets run again, passing in a new parameter to say what button got clicked. So we need to refactor this method slightly to detect that we’ve had a “postback” from the dialog box, and process it appropriately. That makes our Run() method look like this:

protected void Run(ClientPipelineArgs args)
{
    if (args.IsPostBack)
    {
        if (args.Result == "yes")
        {
            string cmd = args.Parameters["cmd"];
            Sitecore.Context.ClientPage.SendMessage(this, cmd);
            return;
        }

        if (args.Result == "no")
        {
            return;
        }
    }
    else
    {
        string msg;
        if (!string.IsNullOrWhiteSpace(args.Parameters["title"]))
        {
            msg = "Are you sure you want to " + 
args.Parameters["title"] + "?";
        }
        else
        {
            msg = "Are you sure you want to do that?";
        }

        SheerResponse.Confirm(msg);
        args.WaitForPostBack();
    }
}

Simple enough change. When the code is run for the first time, we display the dialog. But on the second pass through we check the args.Result value. If the user does not confirm the action then we just return and do nothing else. But if they confirm that they do want the command to run, we use the SendMessage() API call to tell Sitecore to start the original command.

So all that’s left to do is to configure a ribbon button with the confirm command and try it out. The buttons in the Content Editor ribbon are defined in the Core database, at:

/sitecore/content/Applications/Content Editor/Ribbons/Chunks

So you can find your required ribbon button in there and adjust its configuration by wrapping the existing command with the new confirm command. To avoid messing things up while I’m testing, I’ve created a new ribbon button that wraps up the Revert Item command for testing purposes:

image

And with that saved, the ribbon updates and you can test out the command:

image

Success – and we can now wrap any command in the ribbon with a confirmation.

No comments:

Post a Comment