My blog has moved!

You should be automatically redirected in 6 seconds. If not, visit
http://www.f5todebug.wordpress.com
and update your bookmarks.

Friday, 16 January 2009

People Picker for SharePoint Designer Workflow Initiation


Creating a workflow from SharePoint Designer is limiting in many ways, but it's also very quick and easy, and it covers a large majority of every day scenarios. One of the major flaws for me has always been the inability to select 'Person' as a data type for an initiation variable (assuming that this would render a nice people picker control in the initiation form and maybe pass back the UserID to the workflow).

In the last few days I have been determined to resolve this and have come up with a quick and easy fix using some bespoke client side code within the initiation form.

NOTE: In my workflow I actually want to send an email so I am doing a little more in the javascript. This only works because on this project UserID@domain.com is a valid email alias. However the techniques outlined here should assist anyone who needs people data into a workflow.

First of all create a new SPD workflow. Give it a name and select your list. Ensure that 'Allow this workflow to be started manually' is checked (so we get an initation form generated for us).



Select Initiation... and Add...

In the Add Field dialog enter a Field name, in my case 'email' and select 'Single line of text'.



Leave Default value blank and click on Finish. Then OK.

Back in your Workflow Designer click Next.

In this simple example I am just going to send an email, so click Actions and select Send an email. Click on 'this message' to define your email.

Click the address book on the To: line and in the Select Users dialog select Workflow Lookup... and click Add >>.

Define your workflow lookup as Source: Workflow Data and Field: Initiation: email (or whatever you called your initiation variable)



Click OK. Then OK again.

Enter a value in Subject: And also for the body text. Click OK.

Now click Finish as we are done creating this simple workflow.

If you open up the folder structure which SPD has created under Workflows you will see that there should be three files created:

  • the xoml file

  • an xml config file

  • an aspx page

The final one is the one we are interested in as this contains the initiation form for our workflow.

Double click on this so we can take a look.

You will note that essentially this is a standard SharePoint page with a DataFormWebPart which is set up to pass the initiation data to our workflow. In this case we should have a single text box for our initiation variable.

If you look at the code view you will see that various assemblies and namespaces have been included within this page already. Including the Microsoft.SharePoint.WebControls namespace which contains the PeopleEditor control. This is the key to getting people data from the users.

In code view if you put some spaces above the DataFormWebPart definition then we can include this control on the page. Just copy the code below:

<sharepoint:peopleeditor id="pplPicker" runat="server" allowempty="False" validatorenabled="True" onvaluechangedclientscript="SetPeopleValues()" allowtypein="False"></sharepoint:peopleeditor>

Essentially this is adding the control to the page and setting some properties. The really important properties here are:

OnValueChangedClientScript
This property allows us to specify some client side javascript which we wish to run every time the value in the people picker is changed.

AllowTypeIn
This property restricts the user from manually typing into the people picker and forces them to use the Browse dialog to select users. See final note.

Next we need to add the javascript to our page.

At the very top of your page after all the Page and Register tags you will notice the opening <asp:content> tag for PlaceHolderMain. We need to place the following code above this control on the page:

<asp:Content id="Content1" runat="server" contentplaceholderid="PlaceHolderAdditionalPageHead">
<script type="text/javascript">

function SetPeopleValues()
{
//Retrieve the semi-colon separated list of user id's
var people = document.getElementById('ctl00_PlaceHolderMain_pplPicker_downlevelTextBox').value;
document.getElementById('ctl00_PlaceHolderMain_InitiationForm_ff1_1').value = people;
if (people=='')
{
alert('No people selected');}
else
{
//Split the list by ; to get an array of domain user names
var arrPeople = new Array();
arrPeople = people.split(';');

//Create an empty string to hold the email addresses
var strEmails = '';

//Loop through the array of usernames
var arrEndIndex = arrPeople.length - 1;
var i=0;
while (i <= arrEndIndex)
{
//Strip the domain name off and append the email address elements
var person = String(arrPeople[i]);
person = person.toLowerCase().replace('DOMAIN\\','');
var email = person + '@domain.com';

//Add this email address to our string
strEmails = strEmails + email;

//If this is the final email then add this value to the text box in our initiation form control
//Otherwise add the semi-colon
if (i == arrEndIndex)
{
document.getElementById('ctl00_PlaceHolderMain_InitiationForm_ff1_1').value = strEmails;
} else
{
strEmails = strEmails + '; ';
}

i=i+1;
}
}
}

</script>
</asp:Content>


This uses the PlaceHolderAdditionalPageHead place holder to 'inject' some javascript into the head of the html page.

Essentially this code is retrieving the value from our people picker and inserting it into the text box held on the DataFormWebPart. In my case I am doing some additional processing (to generate a semi-colon separated list of email addresses).



NOTE: By default the PeoplePicker control (that is the TextArea html control that is rendered by the PeopleEditor SharePoint control) contains a semi-colon separated list of user names, of the form DOMAIN\UserName, so if you wanted to ignore the string parsing and processing elements of the javascript you could just set this value to the textbox straight away and then do any text processing on these values within your workflow. Like this:

function SetPeopleValues()
{
//Retrieve the semi-colon separated list of user id's
var people = document.getElementById('ctl00_PlaceHolderMain_pplPicker_downlevelTextBox').value;
document.getElementById('ctl00_PlaceHolderMain_InitiationForm_ff1_1').value = people;
}

To tidy up the look and feel of your form select the <tr> tag that contains the textbox control from within your DataFormWebPart and set the style tag to:

style="visibility: hidden"

This will mean that the end user does not see the data being passed to this textbox they just select their users and click Start.

If you save all the changes you have made to this form then try and initiate your workflow you will see that you should be able to select your users using the Browse button and then click start, an email containing the details set out in your workflow should be emailed to the users selected in your People Picker.

And that's it.



NOTE: If you don't declare the PeopleEditor control with AllowTypeIn set to false then your end users could manually type in any name regardless of whether nor not it exists or can be resolved. In this scenario clicking start will not cause the people picker to resolve the names first and the value of your textbox will be empty as our javascript will not run. If anyone has any ideas on this could they please post comments as I would like to get this working.

10 comments:

  1. Thank you Charles. This is exactly what I've been looking for!!!!

    However, how does one put that people picker beside the email text box??

    ReplyDelete
  2. Extremely helpful!!!

    ReplyDelete
  3. Carl, can you send me more detail about what it is you want to achieve. In my example I have hidden the text box as it will contain a confusing list of user id's that I don't want the end user to see.

    ReplyDelete
  4. Charles,
    The onvaluechangedclientscript does not seem to fire when an item is deleted from the people picker text box, leaving the hidden text box in an inconsistent state.

    Did you have the same behavior?

    ReplyDelete
  5. I have not noticed this inconsistent behaviour. I will take a look and let you know what I find.

    ReplyDelete
  6. Hi,

    did you found out something regarding the onvaluechangedclientscript and delete of an entry?

    ReplyDelete
  7. Do you have an answer for the first comment from Charles?

    Problem with the actual solution is, that the people picker is above the workflow title and without any additional information like "Select users".

    ReplyDelete
  8. The onvaluechangedclientscript only fires when the PeopleEditor control tries to resolve the names. This is when the browse button is used to select more users, or when the 'check names' button is used (this is normally used to resolve names which have been entered manually). The solution is to ensure that the 'check names' button is used after the users have been deleted from the box as this will fire your client script.

    I was waiting to see what Carl wanted to achieve before replying to his comment as I was not clear. However as the above comment indicates there is some concern about the positioning of the People Editor control, however this is entirly up to you. The only parts of the DataFormWebPart (the bit that SharePoint adds for you) that are required to be visible on the page are the Start and Cancel buttons. So you can include whatever headings, controls and text you like above this control as long as the other elements within the DataFormWebPart are either deleted (be careful!) or else hidden. My example is showing you how to achieve the desired effect, you should be capable of further customisations on your own, or via other blog posts on the subject.

    I hope people out there are finding this useful, please continue to comment.

    ReplyDelete
  9. Following up in Carl's Question.
    I really want to replace the email text box with the people picker box. At least to the end user. So the text box can jsut be hidden as long as I can place the people picker in it's location on the form. Sharepoint Designer will not let me put the peoplepicker inside the dataWebForm of the text box, only outside.
    I have other fields as well on this initiation form any want them all formated and in a specific order, but with the people picker outside the datawebform I am finding that difficult

    Thanks

    ReplyDelete
  10. As I said before you can hide the entire contents of the DatFormWebPart except the buttons. Assuming you did this then you can add whatever controls above this web part that you require.

    This trick is meant as a simple hack/workaround for those who simply need to be able to use a people picker on workflow initiation and nothing further.

    ReplyDelete

Please leave a comment if you have found this post useful, or if there are any errors. I will do my best to assist if a posted solution does not help with your problem.