Workflow Approval Delegation

#Kentico development #.NET #Kentico Kontent

SHARE

 
In my previous post I presented the approach for configuring workflow approvers on document level, so many documents could reuse the same workflow, having different approvers. In this post I want to suggest minor improvement for the approval process - delegation.

 

There are situations, especially during summer, or winter holidays, when many people take vacation to have a good time with their family and friends. Of course all we really like this period as well as having vacation. However, this might significantly slow down work processes. Let's imagine there is a document, that needs to be approver by 5, or even 10, persons sequentially, but 2 or 3 of those approvers are on vacation - this process might take 3-5 weeks instead of 3-5 days. In some case this is just unacceptable!

So how can we improve this process and allow pages/content to move smoothly through a workflow? Approval delegation should address this issue. Basically there are two steps to enable it: allow users to delegate approvals and handle workflow security appropriately. So let's start!
 

Approval Delegation

In order to configure delegation, first of all, we need to configure time period, we want to delegate our approval rights. I suggest adding two Date fields to the UserSettings class of the Membership module: Vacation Start Date and Vacation End Date. Kentico already has appropriate form control - Calendar, which makes date selection much easier and does not require any development efforts. 
Once we can setup our vacation period, we need to have a possibility to specify who will substitute for us. To address this, we need to add one more field: Delegate Approval To and use user selector as a form control for it. This will provide us with possibility to select any user within the system and, again, with no development efforts.
Next step here is to provide users with the UI to setup delegation. Luckily, users, who approve content, should have access to Kentico Administration as well as to My Profile application within it. If you'll check this application, you'll notice, that new fields already appeared there.

So, congratulations - we are done with this. Let's move forward to workflow security.
 

Workflow Security

Doesn't matter if you have approvers configured on the document level, like described in previous post, or using default workflow configuration you'll have to get all the approvers for the current step and check if any of them is on vacation and, if someone is, retrieve user, who substitutes for original approver, and add him to a set of possible/potential approvers. Let's see how code, that handles this, looks like:

 

[assembly: RegisterCustomManager(typeof(CustomWorkflowManager))]
public class CustomWorkflowManager : WorkflowManager
{
    protected override bool CheckStepPermissionsInternal(TreeNode node, UserInfo user, WorkflowActionEnum action)
    {
        if (action == WorkflowActionEnum.Approve)
        {
            // get users allowed to approve from the document
            var approvers = CacheHelper.Cache(cs => GetDocumentApprovers(document, cs), new CacheSettings(60, string.Format("{0}|{1}", "documentapprovers", document.DocumentID)));
            if (approvers.Contains(user))
            {
                return true;
            }  
        }

        return base.CheckStepPermissionsInternal(node, user, action);
    }

    /// <summary>
    /// Retrieves approvers, or their delegates for a given document
    /// </summary>
    /// <param name="document">Document</param>
    /// <param name="cs">Cache settings</param>
    /// <param name="round">Approval round</param>
    /// <returns>Collection of users, who have to approve document</returns>

    private static List<UserInfo> GetDocumentApprovers(TreeNode document, CacheSettings cs)
    {
        var approvers = GetOriginalDocumentApprovers(document); //--> I'm not providing implementation of this method, as it depends on the particular approach         

        var result = GetDelegates(approvers);

        if ((result != null && result.Count > 0) && cs.Cached)
        {
            cs.CacheDependency = CacheHelper.GetCacheDependency("documentid|" + document.DocumentID);
        }

        return result;
    }

    /// <summary>
    /// Substitutes original document approvers in specified list with their delegates
    /// </summary>
    /// <param name="approvers">Original document approvers</param>
    /// <returns>List of actual approvers</returns>
    private static List<UserInfo> GetDelegates(List<UserInfo> approvers)
    {
        List<UserInfo> delegates = new List<UserInfo>();
        //add approvers, those are not on vacation
        delegates.AddRange(approvers.Where(a => !IsUserOnVacation(a)));

        //add approvers, those are not on vacation
        var onVacation = approvers.Where(a => IsUserOnVacation(a));

        foreach (UserInfo user in onVacation)
        {
            int delegateID = user.GetIntegerValue(Constant.USER_DELEGATE_APPROVAL_FIELD_NAME, -1);
            if (delegateID > 0)
            {
                delegates.Add(UserInfoProvider.GetUserInfo(delegateID));
            }
            // no delegate
            else
            {
                delegates.Add(user);
            }
        }

        return delegates;
    }

    /// <summary>
    /// Checks whether given user is currently on vacation
    /// </summary>
    /// <param name="user">User</param>
    /// <returns>Is on vacation</returns>
    private static bool IsUserOnVacation(UserInfo user)
    {
        var vacationStartDate = user.GetDateTimeValue(Constant.USER_VACATION_START_DATE_FIELD_NAME, DateTime.Now.AddHours(-1));
        var vacationEndDate = user.GetDateTimeValue(Constant.USER_VACATION_END_DATE_FIELD_NAME, DateTime.Now.AddHours(-1));
        return (DateTime.Now >= vacationStartDate && DateTime.Now <= vacationEndDate);
    }
}


So this will take care about workflow security and allow delegate to approve a document. However, this does not cover any other permissions, which means, that delegate should have the same level of access as original approver has besides workflow. This includes access to Administration area, Pages application and document(s).
 

Conclusion

We've got a handy tool that allows us to delegate workflow approval with a minimum effort. I'd like to make a point here that whenever you need to extend Kentico default behavior, you have to look for appropriate way or place to do so, like we did with CustomWorkflowManager in this case. No need to jump on Visual Studio and reinvent the wheel and re-develop entire modules. 

Please feel free to leave your feedback or share your experience with similar problems - we highly appreciate this!

Author

Check other articles

Bitsorchestra
5 5

What our clients say

Bits Orchestra team are outstanding developers​. They listen carefully to our business needs and easily turns our business objectives into a well thought out and executed development effort. Roman is very bright and definitely the most capable developer that has worked on our site. He is not only a Kentico expert but has successfully tackled other complicated development assignments demonstrating expertise in both front and backend development. Roman takes initiative to suggest enhancements that make site maintenance easier while improving the customer experience. The team is very responsive to our work requests and has great follow up. They have also worked very business partners and this has reflected positively on our company. Roman is a true partner for us and a tremendous asset to our organization. We will continue to work with them and would highly recommend Roman and his team for your development needs. He and his team will exceed your expectations!
 Alan Lehmann
Alan Lehmann
President at In energy sector

What our clients say

The Bits Orchestra team does excellent work. They are always available and I appreciate our frequent calls and screen-shares together. Their dedication to the projects and knowledge of Kentico is outstanding. They truly care about the quality of their work, and became a part of our team easily!
Shena Lowe
Shena Lowe
Managing Partner at Consensus Interactive

What our clients say

We hired Roman for a Kentico analysis project and have been very satisfied. He is very skilled and professional. We are looking to hire him and his team again on future projects.
Sylvain Audet
Sylvain Audet
CEO at MyDevPartner.com

What our clients say

Roman and team have taken over an existing Kentico EMS site for a large US Oil Company. So far, they have handled every single request that we have thrown at them and these were diverse, challenging, often bespoke, usually urgent and almost daily, over the last 11 months. Their work is of an extremely high quality, they are capable, quick and we have great confidence in the support that we are getting.
Jon Hollis
Jon Hollis
Head of Web Development at confidential

What our clients say

Bits Orchestra team was very helpful, they had a good understanding of the brief and deep knowledge of the system. They were always keen to provide advice and recommendations that benefit the project substantially.
Ramon Lapenta
Ramon Lapenta
Senior Front End Developer at Cyber-Duck Ltd