Add Support for Content Workflow with Sitecore PXM

Background

Sitecore Print Experience Manager (PXM) is great; you can create dazzling printable documents that reuse your existing Sitecore content. You can enable your designers to add a personal touch to each document, or configure automation to allow document creation on the fly.

One of the primary components of PXM is its InDesign Connector plugin (IDC). It facilitates communication between Sitecore PXM and InDesign, allowing a designer to:

  • add Sitecore content to an existing InDesign document
  • create and update PXM Projects, which are a Sitecore representation of an InDesign document
  • manipulate master documents, which are essentially InDesign templates
  • save content changes directly into Sitecore items

That last feature is what we'll focus on in this post.

The Problem

When saving Sitecore content changes from InDesign, Sitecore PXM does not consider the workflow state of the content item. This causes an issue if the item being edited is in a final workflow state. Out of the box, PXM will make the change directly to the item without creating a new version in draft state. Many governance policies forbid that kind of change.

Solution 1: Disable Saving Content from InDesign

If you want to avoid further customization and also prevent content edits from happening on items in a final workflow state, you could just disable content editing from InDesign altogether. It removes the option from InDesign and does not affect other features of the InDesign Connector.

Simply uncheck the box at

/sitecore/Print Studio/Modules/InDesign connector/Other Settings/Default Settings/Default Settings/Allow content updates from InDesign

Solution 2: Add Workflow Support to Save from InDesign Connector

If you don't mind a little customization, however, you can support workflow on content items when they're saved from the InDesign Connector plugin.

Step 1: Create an item:saving event handler class

public class SaveProcessor{    private Template _StandardTemplate;    private const string PRINT_STUDIO_SITE_NAME = "printstudio";    private static readonly List<Guid> _ActiveItemIds = new List<Guid>();    public void OnItemSaving(object sender, EventArgs args)    {        // Only intercept updates from PXM        if (Context.Site.Name.Equals(PRINT_STUDIO_SITE_NAME))        {            var sitecoreEventArgs = args as SitecoreEventArgs;            var updatedItem = sitecoreEventArgs?.Parameters[0] as Item;            if (updatedItem != null)            {                // If we're already working with this item, allow this save to continue normally (prevents infinite recursion)                if (!_ActiveItemIds.Contains(updatedItem.ID.Guid))                {                    var originalItem = Context.Database.GetItem(updatedItem.ID);                    if (originalItem != null)                    {                        var workflow = Context.Database.WorkflowProvider.GetWorkflow(originalItem);                        var workflowState = workflow?.GetState(originalItem);                        // If the current item is not in workflow, or it is in workflow but the current state is not final, allow the save to continue normally                        if (workflowState != null && workflowState.FinalState)                        {                            var differences = new Dictionary<string, string>();                            foreach (Field field in updatedItem.Fields)                            {                                var updatedItemField = updatedItem.Fields[field.ID];                                var originalItemField = originalItem.Fields[field.ID];                                // Find all the differences that are not standard fields                                if (updatedItemField != null &&                                    !IsStandardField(updatedItemField) &&                                    originalItemField != null &&                                    !updatedItemField.Value.Equals(originalItemField.Value))                                {                                    differences.Add(field.Name, updatedItemField.Value);                                }                            }                            // If there are no differences, allow the save to continue normally                            if (differences.Count > 0)                            {                                // Add this item ID to the list of currently-processing item IDs                                _ActiveItemIds.Add(updatedItem.ID.Guid);                                try                                {                                    originalItem.Editing.BeginEdit();                                    var newVersion = originalItem.Versions.AddVersion();                                    newVersion.Editing.BeginEdit();                                    foreach (var difference in differences)                                    {                                        newVersion[difference.Key] = difference.Value;                                    }                                    newVersion.Editing.EndEdit();                                    originalItem.Editing.EndEdit();                                }                                finally                                {                                    // Remove this item ID from the list of currently-processing item IDs                                    _ActiveItemIds.Remove(updatedItem.ID.Guid);                                }                                sitecoreEventArgs.Result.Cancel = true;                            }                        }                    }                }            }        }    }    public bool IsStandardField(Field field)    {        if (_StandardTemplate == null)            _StandardTemplate = TemplateManager.GetTemplate(Sitecore.Configuration.Settings.DefaultBaseTemplate, field.Database);        return _StandardTemplate.ContainsField(field.ID);    }}

Step 2: Patch your web.config to add the event handler

Make sure you update the type to reference your event handler. This patch configuration file should go in the App_Config\Include folder.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"><sitecore>    <events>        <event name="item:saving">            <handler type="TestPxm.Pxm.SaveProcessor,TestPxm" method="OnItemSaving" patch:before="handler[@type='Sitecore.Tasks.ItemEventHandler, Sitecore.Kernel']"/>        </event>    </events></sitecore></configuration>

Explanation

What is the code doing? Basically, after checking if the content item is in a final workflow state, we add a version to it and make our changes to the new version. We then cancel the original save event to prevent the changes from being made against the version in final workflow state. If it's any other case, we simply allow the save to happen as normal.

Acknowledgements

I wanted to thank my team at Horizontal Integration for their brainstorming, and Mark Demeny of Sitecore for his responsiveness and helpfulness proposing ideas for possible solutions.