Two months ago, I worked on a project where I had to find a solution to chain source Items and their clones together in Sitecore workflow — don’t worry, the clone Items were “locked down” by being protected so content authors cannot make changes to content on the clones — the clones serve as content copies of their source Items for a multi-site solution in a single Sitecore instance.
After some research, a few mistakes — well, maybe more than a few 😉 — and massive help from Oleg Burov, Escalation Engineer at Sitecore USA, I put together a subclass of Sitecore.Workflows.Simple.Workflow — this lives in Sitecore.Kernel.dll — similar to the following:
using Sitecore.Data.Items; using Sitecore.Workflows; using Sitecore.Workflows.Simple; namespace Sitecore.Sandbox.Workflows.Simple { public class ChainSourceClonesWorkflow : Workflow { public ChainSourceClonesWorkflow(string workflowID, WorkflowProvider owner) : base(workflowID, owner) { } public override WorkflowResult Execute(string commandID, Item item, string comments, bool allowUI, params object[] parameters) { WorkflowResult result = base.Execute(commandID, item, comments, allowUI, parameters); foreach (Item clone in item.GetClones()) { base.Execute(commandID, clone, comments, allowUI, parameters); } return result; } } }
The Execute() method above basically moves the passed Item through to the next workflow state by calling the base class’ Execute() method, and grabs all clones for the passed Item — each are also pushed through to the next workflow state via the base class’ Execute() method.
Workflow instances are created by Sitecore.Workflows.Simple.WorkflowProvider. I created the following class to return an instance of the ChainSourceClonesWorkflow class above:
using Sitecore.Workflows; using Sitecore.Workflows.Simple; namespace Sitecore.Sandbox.Workflows.Simple { public class ChainSourceClonesWorkflowProvider : WorkflowProvider { public ChainSourceClonesWorkflowProvider(string databaseName, HistoryStore historyStore) : base(databaseName, historyStore) { } protected override IWorkflow InstantiateWorkflow(string workflowId, WorkflowProvider owner) { return new ChainSourceClonesWorkflow(workflowId, owner); } } }
I then replaced the “out of the box” WorkflowProvider with the one defined above using the following configuration file:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <databases> <database id="master"> <workflowProvider type="Sitecore.Workflows.Simple.WorkflowProvider, Sitecore.Kernel"> <patch:attribute name="type">Sitecore.Sandbox.Workflows.Simple.ChainSourceClonesWorkflowProvider, Sitecore.Sandbox</patch:attribute> </workflowProvider> </database> </databases> </sitecore> </configuration>
Let’s take this for a spin!
I first started with a source and clone in a “Draft” workflow state:
Let’s push the source — and hopefully clone 😉 — through to the next workflow state by submitting it:
As you can see, both are “Awaiting Approval”:
Let’s approve them:
As you can see, both are approved:
If you have any thoughts or comments on this, or know of ways to improve the code above, please drop a comment.
Also, keep in mind the paradigm above is not ideal when content authors are able to make content changes to clones which differ from their source Items. In that scenario, it would be best to let source and clone Items’ workflow be independent.
I suggest using “set” namespace instead of “patch” as it is more clean and easy to read:
I definitely agree with this.
However, I couldn’t get it to work on one client’s instance, and could not figure out why.