Custom Workflow Comments CWC Module for Sitecore 7 Part 2

Posted 11/17/2013 by asura

Part 2 of this blog post will focus on the UI aspects. We will be modifying the display of Comments in the Workbox and the History in the Review tab.

Using a refactor tool like the .NET Reflector or JetBrains dotPeek to decompile Sitecore.Client and grab code for Sitecore.Shell.Applications.Workbox.WorkboxHistoryXmlControl.

Step 1 - Override

To implement this we need to override the functionality in WorkboxHistory.xml. To do this, create the following folders under \Website\sitecore\shell\Override folder:

  • Applications
    • Workbox

Copy WorkboxHistory.xml from \Website\sitecore\shell\Applications\Workbox
To
\Website\sitecore\shell\Override\Applications\Workbox

Step 2 – Code Behind

Use a refactor tool like the .NET Reflector or JetBrains dotPeek to decompile Sitecore.Client and grab code for Sitecore.Shell.Applications.Workbox.WorkBoxHistoryXmlControl.

Add a WorkboxHistoryXmlControl class in your BL or Sitecore Extensions project to mirror the Sitecore.Shell.Applications.Workbox.WorkboxHistoryXmlControl class.

Step 3 - CodeBeside

Modify the WorkboxHistory.xml file in the override folder to replace the value in the CodeBeside tag.

  

Step 4 – Config

Since we are modifying an Xml Control, we need at add our library to the ui\usings and ui\references nodes in the web.config.

Step 5 - Code

In your WorkboxHistoryXmlControl class, modify the OnLoad method to detect custom template comments. See the highlighted rows below:

protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.WriteLanguageCssClass();
            if (!Sitecore.Context.ClientPage.IsEvent)
            {
                IWorkflowProvider workflowProvider = Sitecore.Context.ContentDatabase.WorkflowProvider;
                if (workflowProvider != null)
                {
                    IWorkflow workflow = workflowProvider.GetWorkflow(this.WorkflowID);
                    Error.Assert(workflow != null, string.Concat("Workflow \"", this.WorkflowID, "\" not found."));
                    Item item = Sitecore.Context.ContentDatabase.Items[this.ItemID, Sitecore.Globalization.Language.Parse(this.Language), Sitecore.Data.Version.Parse(this.Version)];
                    if (item != null)
                    {
                        NameValueCollection nameValueCollection = new NameValueCollection();
                        NameValueCollection nameValueCollection1 = new NameValueCollection();
                        WorkflowState[] states = workflow.GetStates();
                        for (int i = 0; i < (int)states.Length; i++)
                        {
                            WorkflowState workflowState = states[i];
                            nameValueCollection.Add(workflowState.StateID, workflowState.DisplayName);
                            nameValueCollection1.Add(workflowState.StateID, workflowState.Icon);
                        }
                        WorkflowEvent[] history = workflow.GetHistory(item);
                        string name = Sitecore.Context.Domain.Name;
                        WorkflowEvent[] workflowEventArray = history;
                        for (int j = 0; j < (int)workflowEventArray.Length; j++)
                        {
                            WorkflowEvent workflowEvent = workflowEventArray[j];
                            string user = workflowEvent.User;
                            if (user.StartsWith(string.Concat(name, "\\"), StringComparison.OrdinalIgnoreCase))
                            {
                                user = StringUtil.Mid(user, name.Length + 1);
                            }
                            string[] strArrays = new string[] { user, Translate.Text("Unknown") };
                            user = StringUtil.GetString(strArrays);
                            string str = nameValueCollection1[workflowEvent.NewState];
                            string[] item1 = new string[] { nameValueCollection[workflowEvent.OldState], "?" };
                            string str1 = StringUtil.GetString(item1);
                            string[] strArrays1 = new string[] { nameValueCollection[workflowEvent.NewState], "?" };
                            string str2 = StringUtil.GetString(strArrays1);
                            string str3 = DateUtil.FormatDateTime(workflowEvent.Date, "D", Sitecore.Context.User.Profile.Culture);
                            XmlControl webControl = Resource.GetWebControl("WorkboxHistoryEntry") as XmlControl;
                            this.History.Controls.Add(webControl);
                            webControl["User"] = user;
                            webControl["Icon"] = str;
                            webControl["Date"] = str3;
                            webControl["Action"] = string.Format(Translate.Text("Changed from {0} to {1}."), str1, str2);

                            //check if the value in the Text field is an ID
                            Sitecore.Data.ID customWorkFlowItemID;
                            if (Sitecore.Data.ID.TryParse(workflowEvent.Text, out customWorkFlowItemID))
                            {
                                //get the rendered text from the fields of the custom template item from the item bucket
                                webControl["Text"] = GetWorkflowItemDetails(customWorkFlowItemID);                                
                            }
                            else
                                webControl["Text"] = workflowEvent.Text;
                        }
                    }
                }
            }
        }

Also we added the GetWorkflowItemDetails method to gather all fields and their values to be rendered on the UI.

    
        /// 
        /// Returns the output with all the fields and their values associated with the custom template item from the item bucket
        /// 
        /// The ID of the custom template item.
        private string GetWorkflowItemDetails(Sitecore.Data.ID customWorkFlowItemID)
        {
            StringBuilder returnString = new StringBuilder();
            //get the item and make sure its not null
            Item customWorkflowItem = Sitecore.Context.ContentDatabase.GetItem(customWorkFlowItemID);
            if (customWorkflowItem != null)
            {
                //loop through all the item fields
                foreach (Field field in customWorkflowItem.Fields)
                {
                    //only extract the template fields and their values
                    if (!(field.Name == "Additional Parameters") && (!(field.Name == "Personalization") || UserOptions.View.ShowPersonalizationSection) && ((!(field.Name == "Tests") || UserOptions.View.ShowTestLabSection) && RenderingItem.IsAvalableNotBlobNotSystemField(field)))
                        returnString.AppendLine(string.Format("{0}: {1}
", field.DisplayName, field.Value)); } } return returnString.ToString(); }

Compile the dll and shown below are the results. 

Step 6 - Magic Code

The class to take over the WorkboxForm does all the heavy lifting. It has the logic to switch between standard functionality and Custom template functionality. The Comments method does the work for us in deciding if a custom template form needs to be displayed and how the results are going to be stored. See the highlighted rows below:

/// 
        /// Comments the specified args.
        /// 
        /// 
        /// The arguments.
        /// 
        public void Comment(ClientPipelineArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (!args.IsPostBack)
            {
                bool showCustom = false;
                //get workflow item
                Item workflowItem = Context.ContentDatabase.GetItem(Context.ClientPage.ServerProperties["workflowid"] as string);

                Template customTemplate = null;
                //check if the custom template field has a value
                if (workflowItem != null && !string.IsNullOrEmpty(workflowItem[new ID(Consts.FieldIDs.CustomTemplate)]))
                {
                    //load custom template based on the field value
                    customTemplate = TemplateManager.GetTemplate(new ID(workflowItem[new ID(Consts.FieldIDs.CustomTemplate)].ToString()),
                        Context.ContentDatabase);

                    //check if it inherits Custom Workflow Comments Base template
                    if (customTemplate != null && customTemplate.InheritsFrom(new ID(Consts.TemplateIDs.CustomWorkflowCommentsBase)))
                            showCustom = true;
                }

                //decide if we show the standard comments dialog or custom dialog
                if (showCustom)
                {
                    if (!new RenderCustomTemplate() { Args = args, ItemID = new ID(Context.ClientPage.ServerProperties["id"].ToString()), CustomTemplateID = customTemplate.ID, Language = Language.Parse(Context.ClientPage.ServerProperties["language"] as string), Version = Sitecore.Data.Version.Parse(Context.ClientPage.ServerProperties["version"] as string) }.Show()) return;
                }
                else
                {
                    //display regular comments dialog
                    Context.ClientPage.ClientResponse.Input("Enter a comment:", string.Empty);
                    args.WaitForPostBack();
                }
                return;
            }
            if (args.Result.Length > 2000)
            {
                Context.ClientPage.ClientResponse.ShowError(new Exception(string.Format("The comment is too long.\n\nYou have entered {0} characters.\nA comment cannot contain more than 2000 characters.", args.Result.Length)));
                Context.ClientPage.ClientResponse.Input("Enter a comment:", string.Empty);
                args.WaitForPostBack();
                return;
            }
            if (args.Result != null && args.Result != "null" && args.Result != "undefined")
            {
                string language = Context.ClientPage.ServerProperties["language"].ToString();
                string version = Context.ClientPage.ServerProperties["version"].ToString();

                //check if the return is text comments or from a custom template
                IEnumerable resultingFields = (IEnumerable)RenderingParametersFieldEditorOptions.Parse(args.Result).Fields;

                string comments = "";
                comments = args.Result;

                //if the result is from a custom template
                if (resultingFields.Count() > 0)
                {
                    //get the item bucket
                    var bucketItem = Context.ContentDatabase.GetItem(Consts.ItemIDs.WorkflowCommentsItemBucket);
                    if (bucketItem != null && BucketManager.IsBucket(bucketItem))
                    {
                        //get workflow item
                        Item workflowItem =
                            Context.ContentDatabase.GetItem(Context.ClientPage.ServerProperties["workflowid"] as string);

                        //check if the custom template field has a value
                        if (workflowItem != null && !string.IsNullOrEmpty(workflowItem[new ID(Consts.FieldIDs.CustomTemplate)]))
                        {
                            // locate item in bucket by name
                            using (
                                var searchContext =
                                    ContentSearchManager.GetIndex(bucketItem as IIndexable).CreateSearchContext())
                            {
                                //ShortID.Encode(item.ID).ToLowerInvariant())
                                string searchCustomWorkflowCommentsItemTemplate = ShortID.Encode(Consts.TemplateIDs.CustomWorkflowCommentsItem).ToLowerInvariant(); 
                                string searchWorkflowItemID = ShortID.Encode(Context.ClientPage.ServerProperties["id"].ToString()).ToLowerInvariant();
                                var result =
                                    searchContext.GetQueryable()
                                        .Where(
                                            x =>
                                                x.Template.Equals(searchCustomWorkflowCommentsItemTemplate) && x.WorkflowItemID == searchWorkflowItemID
                                                && x.Language == language
                                                && x.Version == version) 
                                        .FirstOrDefault();

                                //generate valid unique name
                                string validItemName = ItemUtil.ProposeValidItemName(DateUtil.IsoNow.ToString());
                                
                                Item parent = null;
                                //based on the result, assign parent as the item found or the item bucket
                                if (result != null)
                                {
                                    parent = Context.ContentDatabase.GetItem(result.ID);
                                }

                                //load templates for Custom template and the Custom Workflow Comments Item
                                TemplateItem customTemplate = Context.ContentDatabase.GetTemplate(new ID(workflowItem[new ID(Consts.FieldIDs.CustomTemplate)].ToString()));
                                TemplateItem customWorkflowCommentsItem = Context.ContentDatabase.GetTemplate(new ID(Consts.TemplateIDs.CustomWorkflowCommentsItem));
                                using (new SecurityDisabler())
                                {
                                    if (parent == null)
                                    {
                                        //create an item based on Custom Workflow Comments Item and store the workflow item values
                                        Item newItem = bucketItem.Add(validItemName, customWorkflowCommentsItem);
                                        newItem.Editing.BeginEdit();
                                        newItem.Fields[new ID(Consts.FieldIDs.CustomWorkflowCommentsItem.WorkflowItemID)].Value =
                                            Context.ClientPage.ServerProperties["id"].ToString(); //workflow item id
                                        newItem.Fields[new ID(Consts.FieldIDs.CustomWorkflowCommentsItem.Language)].Value =
                                            language; //language
                                        newItem.Fields[new ID(Consts.FieldIDs.CustomWorkflowCommentsItem.Version)].Value =
                                            version; //version
                                        newItem.Editing.AcceptChanges();

                                       parent = newItem;
                                    }
                                    //generate another valid unique name
                                    validItemName = ItemUtil.ProposeValidItemName(DateUtil.IsoNow.ToString());

                                    //create an item based on the custom template you chose under the Custom Workflow Comments Item
                                    Item customItem = parent.Add(validItemName, customTemplate);
                                    customItem.Editing.BeginEdit();
                                    foreach (FieldDescriptor fieldDescriptor in resultingFields)
                                        customItem.Fields[fieldDescriptor.FieldID].Value = fieldDescriptor.Value;
                                    customItem.Editing.AcceptChanges();

                                    //set comments to the guid of the newly created item
                                    comments = customItem.ID.ToString();
                                }
                            }

                        }
                    }
                }

                IWorkflowProvider workflowProvider = Context.ContentDatabase.WorkflowProvider;
                if (workflowProvider != null)
                {
                    IWorkflow workflow = workflowProvider.GetWorkflow(Context.ClientPage.ServerProperties["workflowid"] as string);
                    if (workflow != null)
                    {
                        ItemRecords items = Context.ContentDatabase.Items;
                        object item = Context.ClientPage.ServerProperties["id"];
                        object empty = item;
                        if (item == null)
                        {
                            empty = string.Empty;
                        }
                        Item item1 = items[empty.ToString(), Language.Parse(Context.ClientPage.ServerProperties["language"] as string), Sitecore.Data.Version.Parse(Context.ClientPage.ServerProperties["version"] as string)];
                        if (item1 != null)
                        {
                            try
                            {
                                workflow.Execute(Context.ClientPage.ServerProperties["command"] as string, item1, comments, true, new object[0]);
                                //workflow.Execute(Context.ClientPage.ServerProperties["command"] as string, item1, args.Result, true, new object[0]);
                            }
                            catch (WorkflowStateMissingException workflowStateMissingException)
                            {
                                SheerResponse.Alert("One or more items could not be processed because their workflow state does not specify the next step.", new string[0]);
                            }
                            UrlString urlString = new UrlString(WebUtil.GetRawUrl());
                            urlString["reload"] = "1";
                            Context.ClientPage.ClientResponse.SetLocation(urlString.ToString());
                        }
                    }
                }
            }
        }

The code and module are published.

The module can be accessed using the url: http://marketplace.sitecore.net/en/Modules/Custom_Workflow_Comments_-_CWC.aspx

I have plans to tweak this module to add more features. If you have any questions or need the source, email me at Akshay dot sura at nttdata dot com.

Share:

Archive

Syndication