“Workflows provide a flexible and controllable way of
content creation, maintenance, and review. Sitecore’s workflow facilities make
it possible to quickly define sophisticated workflows, often without writing
any code at all.”
Workflow is well known feature in the Sitecore, you can
extend/customize it accordingly to the business need, states of the workflow
can be configurable and extend as per the requirement.

Every workflow has the last state called final state, where
Item is ready for publish, Sitecore also have the default publish state called
Approved, with Auto Publish Action, which means once the item is in Approved
State, it can be Auto Publish.

If you’re not familiar with the Sitecore workflow, you can
refer the below posts:


Problem statement

We had the requirement “capability to scheduled publishing during the last state of the workflow”, that’s called
“Publisher”.

Publisher can able to publish the content immediately or can
scheduled for publishing for the future date, for example publisher want to
launch the campaign in the mid night, so they can do the schedule publishing
with the future date.


Solution:

Below Is the high-level solution to achieve the above
requirement:

  1. Provide 2 features to the publisher one is Publish Now and other is Schedule Publish, So the publisher can decide what to do?
  2. If publisher click on Schedule Publish, it will prompt the dialog  to  get the input as “DateTime”
  3. Schedule the item for publish with provided “Datetime”
  4. Run the agent in the background, which will execute in the specific interval (30minutes) and check if scheduled Item is Ready for publishing or not, if yes it will publish the Item.


Below is the step by step implementation:

  • Create the state called Publish in the workflow
  • Create 2 command called Publish now and Schedule Publish in the Publish state as below:

  • Publish Now command simple called the next state called published, which have the default action Auto-publish, which means if the publisher click on Publish now, content will be published directly

  • “Schedule publish” command has the field called “comment Template”, Thanks @Sitecore to provide the Comment Template field, where we can configure the different type of field along with comment field

  • I created the custom Comment template called schedule which has 2 fields comments and “Schedule DateTime “

  • Now, the “schedule publish” command will prompt the comment template fields which we just configured here, in the below image, we can see the 2 command which we have configured in the publish workflow state “Publish Now” and “schedule Publish”



  • This dialog box prompt the schedule datetime, which we can use as the parameter later, so now publisher can have 2 option, publish now and schedule publish and on the click on schedule publish, the above dialog will prompt for schedule datetime
  • Now, if we consider the workflow state called “Publish” and look into the “schedule publish” command, it will call the action named “Schedule” under the next stage called “scheduled”, which mean if the publisher click on the schedule publish button, it prompt the dialog box(comment Template) and call the “Schedule” action.

  • I have configured the custom class function(Sitecore.Foundation.Publishing.ScheduleAction, Sitecore.Foundation.Publishing) in the field called “Type string” in  Schedule” action Item, as highlighted in the above image.
Below Is the code of the “ScheduleAction” function, which
create the queue in the sitecore using the sitecore Item in the specific
folder.

using Sitecore.Data.Items;
using Sitecore.Foundation.SiteSetting.Repositories;
using Sitecore.Workflows.Simple;
using System;
using System.Collections.Generic;
using Sitecore.Tasks;
using System.Linq;
using System.Web;
using Sitecore.Data.Fields;
namespace Sitecore.Foundation.Publishing
{
    public class ScheduleAction
    {
        public void Process(WorkflowPipelineArgs args)
        {
            PushItemToQueue(args);
        }
        public void
PushItemToQueue(
WorkflowPipelineArgs args)
        {
            if (args.DataItem == null)
                return;
            //Read the
comments template field
            if (args.CommentFields[“Schedule
DateTime”
] != null && string.IsNullOrEmpty(args.CommentFields[“Schedule DateTime”]) == false)
            {
                var sourceItem = args.DataItem;
                string scheduleTime = args.CommentFields[“Schedule
DateTime”
];
                //create
the item Name
                string ItemName = ItemUtil.ProposeValidItemName(string.Format(“{0}SchedulePublishQueue”, sourceItem.ID.ToString()));
                using (new
Sitecore.SecurityModel.
SecurityDisabler())
                {
                    // Get the master database
                   Sitecore.Data.Database master = Sitecore.Data.Database.GetDatabase(“master”);
                    // Get the template for which you need to create item
                    Sitecore.Data.Items.TemplateItem template = master.GetItem(new Data.ID(Constants.schedulePublishQueueTemplateId)); //  (“/sitecore/templates/Sample/Sample
Item”);
                    // Get parent Item
                    Item parentItem = master.GetItem(new Data.ID(Constants.schedulePublishQueueParentFolderId));
                    // Add the item to the site tree
                    Item publishQueueItem =
parentItem.Add(ItemName, template);
                    publishQueueItem.Editing.BeginEdit();
                    try
                    {
                        // Assign values to the fields of the new item
                       
publishQueueItem.Fields[
“Publish Item
Id”
].Value = sourceItem.ID.ToString();
                       
publishQueueItem.Fields[
“Publish Item
Path”
].Value = sourceItem.Paths.Path;
                       
publishQueueItem.Fields[
“Publish Schedule
Date”
].Value = scheduleTime;
                        publishQueueItem.Editing.EndEdit();
                    }
                    catch (System.Exception ex)
                    {
                        // Log the message on any failure to sitecore log
                        Sitecore.Diagnostics.Log.Error(“Could not update item “ +
publishQueueItem.Paths.FullPath +
“:
+ ex.Message, this);
                       
publishQueueItem.Editing.CancelEdit();
                    }
                }
              
            }
        }
    }
}



I have created the template called “SchedulePublishQueue”
which will be used at the time of  Sitecore item creation when someone click on
“schedule publish” button(for temporary queuing purpose)



Below is the output of the above function:

The above function creates the Sitecore items using the
template “SchedulePublishQueue” and read the fields from the comment parameter
template and set the value in the SchedulePublishQueue template field for
example the “Publish schedule Date” Field.

Now, in this way, we can create the publishing schedule
queue within the sitecore itself, which is easy to maintain.


Note: we can also
create this queue in the external database as well.


Now everything done, Items has been scheduled, what Next? 
I Need some Job/ Agent/ service which can run in the background, and can configure in the Sitecore.

The moment I thought the sitecore background Job, I remembered the Akshay sura module named “SiteCron”, Yes “SiteCron is the Sitecore module for your everyday scheduling needs” you can find the more details from here,



But I just used the sitecore default agent, As I was in the hurry to implement this POC, so below is the agent that I have configured which will execute in the background with the interval of 1 hour.

<agent type=”Sitecore.Foundation.Publishing.Tasks.PublishScheduleCommand, Sitecore.Foundation.Publishing”             method=”Run” name =”Auto Publish Schedule” interval=”00:01:00″>      </agent>


Basically, this agent will check the schedule publish queue which we just created above, and validate the “Publish schedule Date” time with the current time, If time is less than and equals to the current datetime, it will publish the item and then recycle it, so it will be removed from the scheduled publish queue, so evertime, queue would be clean, below is the code of the agent :

using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.SecurityModel;
using Sitecore.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Sitecore.Foundation.Publishing.Tasks
{
    public class PublishScheduleCommand
    {
        Database webdb = Sitecore.Configuration.Factory.GetDatabase(“web”);
        Database masterdb = Sitecore.Configuration.Factory.GetDatabase(“master”);
        public void Run()
        {
            Item rootFolder = masterdb.GetItem(new Data.ID(Constants.schedulePublishQueueParentFolderId));
            IEnumerable<Item>
unPublishedItem = rootFolder.Axes.GetDescendants().Where
                ( (x => !string.IsNullOrEmpty(x[“Publish Item Id”]) && !string.IsNullOrEmpty(x[“Publish Schedule Date”])
&& 
GetDateTimeFieldValue(x.ID.ToString(),
“Publish
Schedule Date”
) <= DateTime.UtcNow)
                );
            if (unPublishedItem != null && unPublishedItem.Count() > 0)
            {
                foreach (var
ScheduleQueueItem
in
unPublishedItem)
                {
                    //Get Schedule publish Item
                    Item scheduleItem = masterdb.GetItem(new Data.ID(ScheduleQueueItem[“Publish Item Id”]));
                   
PublishScheduleItem(scheduleItem, ScheduleQueueItem);
                }
            }
           
        }
        /// <summary>
        /// by: Ashish Bansal
        /// For: Publish the schedule sitecore Item, Recyle the schedule
queue and change the workflow state
        /// </summary>
        /// <param name=”actualPublishItem“></param>
        /// <param name=”ScheduleQueueItem“></param>
        private void
PublishScheduleItem(
Item
actualPublishItem,
Item ScheduleQueueItem)
        {
            try
            {
                using (new
Sitecore.SecurityModel.
SecurityDisabler())
                {
                    // target databases
                    Database[] databases = new Database[1] { webdb };
                    Sitecore.Handle publishHandle =
Sitecore.Publishing.
PublishManager.PublishItem(actualPublishItem, databases, webdb.Languages, false, false);
                   
                    // change the workflow state
                    actualPublishItem.Editing.BeginEdit();
                    actualPublishItem[“__Workflow state”] = Constants.publishedWorkflowStateID;
                   
actualPublishItem.Editing.EndEdit();
                   
ScheduleQueueItem.Editing.BeginEdit();
                    ScheduleQueueItem[“Published DateTime”] = DateUtil.ToIsoDate(DateTime.Now);
                    ScheduleQueueItem[“Is Published”] = “true”;
                   
ScheduleQueueItem.Editing.EndEdit();
                    //remove from the Publishing Queue
                   
ScheduleQueueItem.Recycle();
                }
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(“Sitecore.Foundation.Publishing.Tasks.PublishScheduleCommand.PublishScheduleItem” + ” Error in” +   ex.Message, this);
            }
          
          
        }
        private DateTime GetDateTimeFieldValue(string currentItemID, string fieldName)
        {
            Sitecore.Data.Database master = Sitecore.Data.Database.GetDatabase(“master”);
            Item currentItem = master.GetItem(new Data.ID(currentItemID));
            DateField fieldValue = currentItem.Fields[fieldName];
            DateTime publishDate = DateTime.MinValue;
            if ((fieldValue != null) && (fieldValue.InnerField.Value != null))
                publishDate = DateUtil.IsoDateToDateTime(currentItem.Fields[fieldName].Value);
            return publishDate;
        }
    }
}

Note: There is a scope of optimization in the above solution, this is just for POC purpose, like I said, we can use “SiteCron” for the background Job, we can maintain the queue in the external system, we can provide more features, Code optimization( using Index search API to get the schedule queue Items) etc, etc..

I hope this article will help you for schedule publishing in the workflow:


 Happy Sitecoring 

Leave a Reply

Your email address will not be published. Required fields are marked *