I was scavenging through my local sandbox instance’s Web.config the other day — yes I was looking for things to customize — and noticed the getQueryState pipeline — a pipeline that contains no “out of the box” pipeline processors:
<?xml version="1.0" encoding="utf-8"?> <configuration> <!-- Some stuff here --> <sitecore> <!-- Some more stuff here --> <pipelines> <!-- Even more stuff here --> <!-- Allows developers to programmatically disable or hide any button or panel in the Content Editor ribbons without overriding the individual commands. Processors must accept a single argument of type GetQueryStateArgs (namespace: Sitecore.Pipelines.GetQueryState) --> <getQueryState> </getQueryState> <!-- Yeup, more stuff here --> </pipelines> <!-- wow, lots of stuff here too --> </sitecore> <!-- lots of stuff down here --> </configuration>
The above abridged version of my Web.config contains an XML comment underscoring what this pipeline should be used for: disabling and/or hiding buttons in the Sitecore client.
Although I am still unclear around the practicality of using this pipeline overall — if you have an idea, please leave a comment — I thought it would be fun building one regardless, just to see how it works. Besides, I like to tinker with things — many of my previous posts corroborate this sentiment.
What I came up with is a getQueryState pipeline processor that disables buttons containing move related commands on a selected item in the content tree with a particular template. Sorting commands also fall under the umbrella of move related commands, so these are included in our set of commands to disable.
Here’s the pipeline processor I built:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sitecore.Configuration; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Pipelines.GetQueryState; using Sitecore.Shell.Framework.Commands; namespace Sitecore.Sandbox.Pipelines.GetQueryState { public class DisableMoveCommands { private static readonly IEnumerable<string> Commands = GetCommands(); private static readonly IEnumerable<string> UnmovableTemplateIDs = GetUnmovableTemplateIDs(); public void Process(GetQueryStateArgs args) { if (!CanProcessGetQueryStateArgs(args)) { return; } bool shouldDisableCommand = IsUnmovableItem(GetCommandContextItem(args)) && IsMovableCommand(args.CommandName); if (shouldDisableCommand) { args.CommandState = CommandState.Disabled; } } private static bool CanProcessGetQueryStateArgs(GetQueryStateArgs args) { return args != null && !string.IsNullOrEmpty(args.CommandName) && args.CommandContext != null && args.CommandContext.Items.Any(); } private static Item GetCommandContextItem(GetQueryStateArgs args) { Assert.ArgumentNotNull(args, "args"); Assert.ArgumentNotNull(args.CommandContext, "args.CommandContext"); Assert.ArgumentNotNull(args.CommandContext.Items, "args.CommandContext.Items"); return args.CommandContext.Items.FirstOrDefault(); } private static bool IsUnmovableItem(Item item) { Assert.ArgumentNotNull(item, "item"); return IsUnmovableTemplateID(item.TemplateID); } private static bool IsUnmovableTemplateID(ID templateID) { Assert.ArgumentCondition(!ID.IsNullOrEmpty(templateID), "templateID", "templateID must be set!"); return UnmovableTemplateIDs.Contains(templateID.ToString()); } private static bool IsMovableCommand(string command) { Assert.ArgumentNotNullOrEmpty(command, "command"); return Commands.Contains(command); } private static IEnumerable<string> GetCommands() { return GetStringCollection("moveCommandsToPrevent/command"); } private static IEnumerable<string> GetUnmovableTemplateIDs() { return GetStringCollection("unmovableTemplates/id"); } private static IEnumerable<string> GetStringCollection(string path) { Assert.ArgumentNotNullOrEmpty(path, "path"); return Factory.GetStringSet(path); } } }
The above pipeline processor checks to see if the selected item in the content tree has the unmovable template I defined, coupled with whether the current command is within the set of commands we are to disable. If both are cases are met, the pipeline processor will disable the context command.
I defined my unmovable template and commands to disable in a patch include config file, along with the getQueryState pipeline processor:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <getQueryState> <processor type="Sitecore.Sandbox.Pipelines.GetQueryState.DisableMoveCommands, Sitecore.Sandbox" /> </getQueryState> </pipelines> <unmovableTemplates> <id>{8ADD3F45-027C-49C5-A8FB-0406B8C8728D}</id> </unmovableTemplates> <moveCommandsToPrevent> <command>item:moveto</command> <command>item:cuttoclipboard</command> <command>item:moveup</command> <command>item:movedown</command> <command>item:movefirst</command> <command>item:movelast</command> <command>item:moveto</command> </moveCommandsToPrevent> </sitecore> </configuration>
Looking at the context menu on my unmovable item — appropriately named “You Cant Move This” — you can see the move related buttons are disabled:
Plus, item level sorting commands are also disabled:
Move related buttons in the ribbon are also disabled:
There really wasn’t much to building this pipeline processor, and this pipeline is at your disposal if you ever find yourself in a situation where you might have to disable buttons in the Sitecore client for whatever reason.
However, as I mentioned above, I still don’t understand why one would want to use this pipeline. If you have an idea why, please let me know.
Nice investigative work there Mike.
I can see a few good cases for use of this pipeline, when you want to stop all users (including admin) from deleting, moving or renaming critical site items such as the configuration/settings item, the error item or the search item. Any of the items that would break the site if they couldn’t be found. Outside that example, anything that requires logic to determine if the user should have permission that cannot be resolved through standard Sitecore security.
Thanks Alistair!
[…] turns out one can use the getQueryState pipeline. Found this out through this excellent post by @MikeReynolds. Thanks for […]
We were hoping to use this to lock the publish buttons if the user selects the “/Sitecore/Content” or “/Sitecore” nodes. Unfortunately clicking another node to publish disables the publish dialog box from displaying so the user cannot select publishing options before publishing anymore.
Are you able to reach out to me on the Sitecore Community Slack with this?
That way, we could have a better discussion and maybe exchange code snippets.
http://bit.ly/SitecoreSlackSignup
Can you explain how the “Process” method gets called? As it’s not an overridden method, it’s not part of the standard Sitecore execution path.
Thanks.
It’s the default method called for Pipeline processors when you don’t define one on your processor config.