Apply Content Approval Workflows by Page Type and Block’s Folder Name

Content Approval or workflows are quite useful in a business scenario with several departments which one control over the content to be published in a site. EPiServer provides a utility in the CMS editor which allows to edit a workflow for a page or a folder and then structure the site in a way the other pages will inherit the parent workflow. This works for most cases, but sometimes when you have several hundred different pages which requires a different workflow it will get cumbersome and hard to maintain.

d1.png

To use the EPiServer default tool you must right click in the page or folder and then click on Edit Approval Sequence. This will show the configuration page above where you can add steps and roles or users as you want. Remember that blocks cannot have edit approval sequences, but the folders which contains them can have.

d2.png

Today, I will try to solve this problem using a different approach which will allow the developer to add as many page types and folders to a content approval sequence defined by code. This is by far not the best solution for the problem and I am planning to release a plugin which takes into account more dynamic behaviors. The steps to solve this problem are the following:

  1. Create an enumeration to define the possible coded workflows
  2. Create an approval helper to set approval contents to pages and folders
  3. Add an initialization module that listens to the create content event/li>

So once again without more hustle lets code !!!

Create an enumeration to define the possible coded workflows

We will begin creating the enumeration to identify the different types of workflows.


    public enum WorkflowType
    {
        First,
        Second,
        Third,
        Fourth
    }

Create an approval helper to set approval contents to pages and folders

Now we will create a approval helper class which will allow us to set content approvals to different page types and folders. The steps and the reviewers for each step are going to be hard coded. Pay special attention to the comments inside the class.


    public class ContentApprovalHelper
    {
        // You can set each reviewer to an specific language or all of them
        private static CultureInfo[] lang = new CultureInfo[] { CultureInfo.InvariantCulture };
        private static List firstWorkflowSteps = new List
        {
            new ApprovalDefinitionStep("Send to Step 1", new ApprovalDefinitionReviewer[]
            {
                new ApprovalDefinitionReviewer("Administrators", lang, ApprovalDefinitionReviewerType.Role)
            }),
            new ApprovalDefinitionStep("Send to Step 2", new ApprovalDefinitionReviewer[]
            {
                new ApprovalDefinitionReviewer("Administrators", lang, ApprovalDefinitionReviewerType.Role)
            })
        };

        public static void CreateContentApproval(ContentReference contentLink, WorkflowType workflowType)
        {
            IApprovalDefinitionRepository definitionRepository = ServiceLocator.Current.GetInstance();

            // Gets the latest version of a definition using a ContentReference. 
            var definition = definitionRepository.GetAsync(contentLink).Result;

            if (definition != null)
            {
                return;
            }

            // Get the steps by the workflow type
            var steps = GetStepsByWorkflowType(workflowType);

            // Creates a content approval definition
            definition = new ContentApprovalDefinition
            {
                ContentLink = contentLink,
                Steps = steps,
                RequireCommentOnApprove = true,
                RequireCommentOnReject = true,
                IsEnabled = true
            };

            // Saves the definition
            definitionRepository.SaveAsync(definition).ConfigureAwait(false);
        }
     
        // You can add more hard coded steps over here
        private static List GetStepsByWorkflowType(WorkflowType workflowType)
        {
            switch (workflowType)
            {
                case WorkflowType.First:
                    return firstWorkflowSteps;
                case WorkflowType.Second:
                    break;
                case WorkflowType.Third:
                    break;
                case WorkflowType.Fourth:
                    break;
                default:
                    break;
            }

            return new List();
        }
    }

Calling the method CreateContentApproval will set a content reference page or folder with a content approval definition.

Add an initialization module that listens to the create content event

Finally, we will add an initialization module which hears the create content event. This will allow us to set the content approval workflow every time a page or block is created and decide if the item must have a content approval or not. Once again pay special attention to the comments inside the class.


    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ContentApprovalPageCreationConfig : IInitializableModule
    {
        public IContentRepository _repository;
        private IContentEvents _contentEvents;

        public void Initialize(InitializationEngine context)
        {
            // Initialize the content events and content repository classes
            if (_contentEvents == null)
            {
                _contentEvents = ServiceLocator.Current.GetInstance();
            }

            if (_repository == null)
            {
                _repository = ServiceLocator.Current.GetInstance();
            }

            // Listen to the create content event
            _contentEvents.CreatedContent += ContentEvents_CreatedContent;
        }

        public void Uninitialize(InitializationEngine context)
        {
            if (_contentEvents == null)
            {
                _contentEvents = ServiceLocator.Current.GetInstance();
            }

            // Remove listen to the create content event
            _contentEvents.CreatedContent -= ContentEvents_CreatedContent;
        }

        private void ContentEvents_CreatedContent(object sender, ContentEventArgs e)
        {
            if (e.Content is LocalPage || e.Content is OurServicesPage || 
                e.Content is WhoAreWePage || e.Content is ChooseUsPage || 
                e.Content is StandardPage)
            {
                ContentApprovalHelper.CreateContentApproval(e.ContentLink, WorkflowType.First);
            }

            var localFolder = _repository.GetAncestors(e.ContentLink).FirstOrDefault(x => x.Name == ConfigurationManager.AppSettings["Block_Folder_Name"]);
            if (e.Content is BlockData && localFolder != null)
            {                ContentApprovalHelper.CreateContentApproval(localFolder.ContentLink, WorkflowType.First);
            }
        }
    }

ConfigurationManager.AppSettings[“Block_Folder_Name”] is a web.config key which have the folder name that will be applied with the content approval definition if and only if the block was created inside the specified folder.

And that is all. Every time the user goes to the CMS editor and create a page with one of the types specified in the initialization module or a block inside the folder defined in the web.config file; the page or block will have the first content approval definition and must follow the process to be published.

I hope it will help someone and as always keep learning !!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s