Sitecore Commands: A Developer’s Friend

July 12, 2017

Blog | Development | Sitecore Commands: A Developer’s Friend

Every once in a while, a need comes along for triggering custom functionality on demand.  Sitecore has got it covered with their concept of commands.  There are a handful of ways to trigger Sitecore commands like Context menus and custom ribbon buttons.  For example sake, I will focus on a Sitecore’s custom ribbon command.

With all commands, there are a few things we will need to cover:

The Command code:

public class YourCustomCommand : Sitecore.Shell.Framework.Commands.Command
{
    public override void Execute(CommandContext context)
    {
        var contextItem = context.Items.FirstOrDefault();

        if (contextItem == null)
            return;

        ///example parameters being passed in
        var contextItemFieldId = context.Parameters["contextItemFieldId"];
        var sourceFieldId = context.Parameters["sourceFieldId"];
        var includeChildren = context.Parameters["includeChildren"];

        ///setup the progressBox to execute the CustomLogic asynchronusly
        Sitecore.Shell.Applications.Dialogs.ProgressBoxes.ProgressBox.Execute(
                "Your Custom Command",
                "Your Custom Command",
                CustomLogic,
                new object[] { contextItem, sourceFieldId, includeChildren});
        }
}

public void CustomLogic(params object[] parameters)
{
   ///insert your needed logic
}

Above, you will see some example code illustrating how to execute some custom code via a command.  In this example, the command accepts 3 parameters that can be passed in and will raise the ProgressBox modal dialog to illustrate work being done.

The config file:

<configuration xmlns:x="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <commands>
            <command name="yourSite:YourCustomCommand"
                   type="YourSite.CustomSitecore.YourCustomCommand , YourSite.CustomSitecore"/>
        </commands>
    </sitecore>
</configuration>

The above config file will most likely be included within the App_Config/Include somewhere and is commonly named Yoursite.CustomCommands.config or something similar.  The main goal of this config is to map the yourSite:YourCustomCommand command to the proper class and namespace of code needed to actually execute it.

The Sitecore Item:

The above screenshot shows an example command button that will render in the Operations chunk, commonly found on the Home tab.  You can choose to leverage existing chunks or you can go thru, and entirely create your own custom tab and chunk(s), but for the sake of simplicity we will do the former.  The ribbon button item in the content tree under Operations is of template /sitecore/templates/system/Ribbon/Small Button, but there are a handful of other ribbon button types that will work.  The last and most important piece of this item is the Click function.  This is where you will drop in the yourSite:YourCustomCommand and also be able to pass in values as parameters.  This example has string values being passed into the parameters, but values like $(id) can be passed in as well which should resolve to the ID of the current context item.

Given the above, you should have hopefully enough to create a custom command, a ribbon button to trigger said command, and the ability to pass in parameters to make your command more flexible.

Execution as a Job

Now, given that we are using the ProgressBox functionality, our command will execute asynchronously which is great, unless you want do something like refresh the current item because you updated values for fields or if you want to update all the children items underneath the current item.  You can achieve a similar look and feel to the ProgressBox leveraging some of the Sitecore.Jobs functionality.  To give you a proper example, I have included a JobsBase class that you can use:

public class JobsBase
{
    private readonly string _prefix;
    private readonly string _siteName;

    public object[] Parameters { get; private set; }
    public JobsBase(string jobName, object[] parameters, string siteName = "shell")
    {
        _prefix = jobName;
        _siteName = siteName;
        Parameters = parameters;
    }

    public void SetMessageAndLog(string message)
    {
        var finalMessage = string.IsNullOrWhiteSpace(_prefix) ? message : _prefix + " : " + message;
        Sitecore.Context.Job.Status.Messages.Add(finalMessage);
        Log.Info(finalMessage, this);
    }

    public JobStatus Status {
        get
        {
            return Sitecore.Context.Job.Status;
        }
    }

    public void StartJob(string postExecuteCommand, string icon = "Applications/32x32/window_add.png")
    {
        var verifyMethodExists = Sitecore.Reflection.ReflectionUtil.GetMethod(this, "Execute", Parameters);
        if(verifyMethodExists == null)
            throw new NotImplementedException(string.Format("{0} does not implement the 'Execute' method, or the parameters provided do not match the method signature", this));

        JobOptions jobOption = new JobOptions(_prefix, "UI", _siteName, this, "Execute", Parameters);
        Job job = JobManager.Start(jobOption);
        string handle = job.Handle.ToString();
        LongRunningOptions longRunningOption = new LongRunningOptions(handle)
            {
                Title = _prefix,
                Icon = icon,
                Threshold = 0,
                Message = postExecuteCommand
            };
         longRunningOption.ShowModal();
    }
}

Within the scope of the above example, we should only need to tweak our initial command and CustomLogic method slightly. What we want to do is take what was previously in CustomLogic as far as what you actually want to execute and extract that out to its own class like so:

public class CustomLogicJob : JobsBase
{
    private Item _rootItem;
    private string _contextItemFieldId;
    private bool _includeChildren;
    private string _sourceFieldId;

    public CustomLogicJob(object[] array) : base("Custom Logic Job", array){}
    public void Execute(Item rootItem, string contextItemFieldId, bool includeChildren, string sourceFieldId)
    {
        _rootItem = rootItem;
        _contextItemFieldId = contextItemFieldId;
        _includeChildren = includeChildren;
        _sourceFieldId = sourceFieldId;
        RunLogic(_rootItem);
    }

    private void RunLogic(Item item)
    {
        //performing logic that was once in the CustomLogic method
        //but added some item.Editing just to show its usage
        item.Editing.BeginEdit();
        try
        {
            //Do things to items as needed foreach, update Status.Processed 
            item.Editing.EndEdit();
        }
        catch (Exception)
        {
            item.Editing.CancelEdit();
        }
        Status.State = JobState.Finished;
    }
}

Now that the CustomLogic is abstracted out to its own class, we just need to invoke it from the command which will take some modifying from the previous snippet:

public class YourCustomCommand : Sitecore.Shell.Framework.Commands.Command
{
    public override void Execute(CommandContext context)
    {
        var contextItem = context.Items.FirstOrDefault();

        if (contextItem == null)
            return;

        ///example parameters being passed in
        var contextItemFieldId = context.Parameters["contextItemFieldId"];
        var sourceFieldId = context.Parameters["sourceFieldId"];
        var includeChildren = context.Parameters["includeChildren"];

        ///execute the custom logic method, now will run as a job
	CustomLogic(new object[] { contextItem, contextItemFieldId, includeChildrenBool, sourceFieldId });
    }
}

public void CustomLogic(params object[] parameters)
{
	var proc = new CustomLogicJob(parameters);
	var currentItem = (Item)parameters[0];
	//below is a message/command to be executed after the job is complete
	//in this case we want the item to simply reload
	string refreshCurrentItem = string.Format("item:load(id={0},language={1},version={2})", currentItem.ID, currentItem.Language, currentItem.Version);
	proc.StartJob(refreshCurrentItem);
}

To summarize, the above snippet will execute the needed command logic using the Sitecore.Jobs classes, particularly LongRunningOptions which will allow us to pass a message for the job to execute after completion like

string refreshCurrentItem = string.Format("item:load(id={0},language={1},version={2})", currentItem.ID, currentItem.Language, currentItem.Version);

or

string refreshChildren = string.format("item:refreshchildren(id={0})", currentItem.Id);

Conclusion: Go try Sitecore Commands today!

Sitecore has some great constructs in place for executing functionality on demand thru commands, both asynchronously thru ProgressBox and synchronously thru Jobs.  Hopefully, this post provides helpful insight and snippets you can utilize to make your job easier todayl!

Steve VandenBush

Technical Lead
Tags
  • Development
  • Sitecore
  • Web Development
subscribe to GeekHive's newsletter

Never miss out on a Sitecore update again!

Our newsletter features a blog roundup of our top posts so you can stay up to date with industry trends, tutorials, and best practices.

Recent Work

Check out what else we've been working on