Tuesday, June 25, 2013

Project Server - Get approver's username in workflow

In my previous posts, I mentioned many times the benefits of using Demand Management workflow for Project Server from Solution Starters project (I described it in this post).

I am very satisfied with its possibilities, but one flaw was really bothering me. Problem is, you can not fetch the username of approver of a stage in workflow. You can get to any sort of workflow data, but this approver information seems to be hidden.

But, after hours of searching,I managed to find it. I must say, that, this isn't the nicest solution, but I think it is the only one and this works 100 %. If you know any other way, please, let me know.

Approver's username is hidden in OfficeTask, in ConsolidatedComments. But, it is mixed with bunch of other stings, so, I extracted with this method:

//This is the method for reading Comments and extracting the username


private static string getApproversUsername()
{

    var officeTask = (OfficeTask)((Activity)sender).Parent.Parent.Parent;


    string[] _userComment = officeTask.ConsolidatedComments.Split(new string[] { "Comment:" }, StringSplitOptions.None);

    IEnumerable<string> _text = null;
    if (_userComment[_userComment.Length - 2].Contains("Approved by "))
    {
        _text = GetSubStrings(_userComment[_userComment.Length - 2], "Approved by ", " on ");
    }
    else
    {
        _text = GetSubStrings(_userComment[_userComment.Length - 2], "Rejected by ", " on ");
    }

    string _username = "";

    foreach (string item in _text)
    {
        _username = item;
    }
    
    return _username;
}


//This is regex for extracting the desired string

private static IEnumerable<string> GetSubStrings(string inputComment, string start, string end)
{
     Regex r = new Regex(Regex.Escape(start) + "(.*?)" + Regex.Escape(end));
     MatchCollection matches = r.Matches(inputComment);
     foreach (Match match in matches)
         yield return match.Groups[1].Value;
}
 

       
I know that this looks pretty odd, but, it works. I'll try to explain what I am doing here. 

Let's say that we have a task that needs to be approved (or rejected) by 3 different approvers. First user (let's say his name is Rajesh Koothrappali :) ) approves task. 
Now, ConsolidatedComments field looks something like this:

"Approved by Rajesh Koothrappali on 05/05/2013"

These methods will extract Rajesh Koothrappali from this string.

Now, second user (let's say his name is Howard Wolowitz) approves the task. Now, ConsolidatedComments field looks something like this (it is appended to the last string): 

"Approved by Rajesh Koothrappali on 05/05/2013;Approved by Howard Wolowitz on 05/05/2013"

Our methods will now extract Howard Wolowitz from this string.

Finally, third user, Sheldon Cooper comes and he rejects the task. Now, ConsolidatedComments field looks something like this (it is appended to the last string):

"Approved by Rajesh Koothrappali on 05/05/2013;Approved by Howard Wolowitz on 06/05/2013;Approved by Sheldon Cooper on 07/05/2013;"

Our methods will now return Sheldon Cooper as approver.


As you can see, we are always fetching the last username in that string. Hope you will find it helpful.