Wednesday, May 1, 2013

Custom Workflow Activity for SharePoint Designer to Retrieve Manager

My recent task was to create a custom workflow activity that users could access in SharePoint Designer which would be used to retrieve a users manager from Active Directory.  No, you don't get this out of the box.  But you almost do!  And with this post it should be taken care of.
I won't cover the basics for how to create a custom activity and deploy it for use with SPD.  There are many other posts that cover those details.  And speaking of being lazy, turns out there is a code sample in the ECM Starter Kit that is a great starting point for exactly what I was trying to accomplish (download it, install it, sample located in "ECM Starter Kit\Code Samples\Workflow\ECMActivities").  Through a lot of trial and error (no debug capability in clients dev site) I was able to modify it to be a bit more stable.  I added logic to handle things like AD properties not existing and manager name being in a couple different formats ({First Name} {Last Name} and {Last Name}, {First Name}). 
I'll post the code for the SetManagerFields method as that's where all the customization takes place.
    public void SetManagerFields(object sender, EventArgs e)
    {
        try
        {
            SearchSuccessful = false;

            //Set up the AD objects
            DirectoryEntry dirEntry = new DirectoryEntry("GC:");
            if (null == dirEntry)
            {
                Outcome = "Could not connect to Directory";
                return;
            }

            System.Collections.IEnumerator gcChildrenEnum = dirEntry.Children.GetEnumerator();
            gcChildrenEnum.MoveNext();

            //Search for Manager of AccountName
            DirectorySearcher searcher = new DirectorySearcher((DirectoryEntry)gcChildrenEnum.Current);
            string filterString = "(samAccountName= " + AccountName.Substring(AccountName.IndexOf("\\") + 1) + ")";
            searcher.Filter = filterString;
            SearchResult result = searcher.FindOne();

            if (result == null)
            {
                Outcome = "Could not find user account.";
                return;
            }

            if (result.Properties.Contains("manager"))
            {
                //Parse manager display name from search result
                ManagerDisplayName = result.Properties["manager"][0].ToString();
                int index;
                int indexComma = ManagerDisplayName.IndexOf(',');
                int indexSlash = ManagerDisplayName.IndexOf('\\');
                //If a comma is escaped, grab the second comma to get the full display name
                if (indexSlash != -1 && indexComma > indexSlash)
                    index = ManagerDisplayName.IndexOf(',', indexComma + 1);
                else
                    index = indexComma;

                ManagerDisplayName = ManagerDisplayName.Substring(3, index - 3);
                //If there's a slash, remove it (.Replace("\\", "") doesn't work)
                if (indexSlash != -1)
                {
                    //Find it again cause it moved
                    indexSlash = ManagerDisplayName.IndexOf('\\');
                    indexComma = ManagerDisplayName.IndexOf(',');

                    ManagerDisplayName = ManagerDisplayName.Substring(0, indexSlash) +
                        ManagerDisplayName.Substring(indexComma);
                }

                searcher.Filter = "(displayName= " + ManagerDisplayName + ")";
                result = searcher.FindOne();
                if (result == null)
                {
                    Outcome = "Could not find manager " + ManagerDisplayName + ".";
                    return;
                }

                //Propagate data to the promoted properties
                if (result.Properties.Contains("samaccountname"))
                    ManagerAccountName = result.Properties["samaccountname"][0].ToString();
                if (result.Properties.Contains("mail"))
                    ManagerEmailAddress = result.Properties["mail"][0].ToString();
                Outcome = "Manager retrieved successfully.";
                SearchSuccessful = true;
            }
            else
            {
                Outcome = "AD does not contain a manager property";
                return;
            }
        }
        catch (Exception ex)
        {
            Outcome = "Error: " + ex.Message;
        }
    }
Pretty nice, right?  I know, most of that I didn't even write.  I'll shut up.  But...Now when a user uses this activity in their workflow in SPD, they get a nice response back even if the manager property isn't even there (which is possible depending on the quality of the directory.)  After running the action, they can check the IsSuccess variable to determine if the manager was found.  Then act accordingly.

No comments:

Post a Comment