Thursday, June 26, 2014

SharePoint 2013 ClientPeoplePicker Using REST

One of the benefits of using the SPRemoteAPIExplorer extension  is you can run across functionality that you did not know existed in the SharePoint REST API. Many developers use CSOM/JSOM to implement the PeoplePicker in their applications.  Both Richard diZerega’s  Real World Apps for SharePoint 2013 and Jeremy Thake’s  Using Multiple PeoplePickers in SharePoint 2013 Hosted Apps with AngularJS are great articles explaining how to do this using JSOM.  Many developers have asked if there is a simpler way of implementing a PeoplePicker without having to load the dependent JavaScript files.  This has been difficult with provider hosted apps since by default it does not include  an app web. You can get a sample of how to add  the people picker control to a provider hosted app from the Office App Model Samples v2.0 in the Components\Core.PeoplePicker folder AMS App Model Samples.  There is also the experimental office widget that implements a people picker control for provider hosted apps Widgets for Provider Hosted Apps. The good news is that you can implement your own people picker using the “_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser” endpoint along with jquery-UI. It does not require having to load any of the dependent js files and makes it very easy to implement multiple people pickers. In this post I will show you sample code on how you can set up one ajax rest function to service mulitple jquery-ui autocomplete text boxes. The endpoint gives you maximum flexibility on how to control the searching for users and I will explain what each parameter means.

Picking Apart the People Picker

The above shows two jquery-ui  autocomplete text boxes both using the same function to suggest people. The first box is configured to allow suggesting users and groups and the second one only allows suggesting users.  This was easy to implement by just adding two input elements to page and then in the javascript attaching the function to do the search using jquery-ui.

$(document).ready(function () {
$("#txtPeoplePicker").autocomplete({
source: search,
minLength: 2
});
$("#txtPeoplePicker2").autocomplete({
source: search,
minLength: 2
});
});

The code below shows the search function that is called which is using the REST API to get the suggestions. Note that I added a principalType attribute to each html input element to allow changing the behavior of the of what is returned. You can add as many attributes you want and use them in your function to change any of the query parameters.

function search(request,response) {
var appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
var hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));

var restSource = appweburl + "/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser";
var principalType = this.element[0].getAttribute('principalType');
$.ajax(
{
'url':restSource,
'method':'POST',
'data':JSON.stringify({
'queryParams':{
'__metadata':{
'type':'SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters'
},
'AllowEmailAddresses':true,
'AllowMultipleEntities':false,
'AllUrlZones':false,
'MaximumEntitySuggestions':50,
'PrincipalSource':15,
'PrincipalType': principalType,
'QueryString':request.term
//'Required':false,
//'SharePointGroupID':null,
//'UrlZone':null,
//'UrlZoneSpecified':false,
//'Web':null,
//'WebApplicationID':null
}
}),
'headers':{
'accept':'application/json;odata=verbose',
'content-type':'application/json;odata=verbose',
'X-RequestDigest':requestDigest
},
'success':function (data) {
var d = data;
var results = JSON.parse(data.d.ClientPeoplePickerSearchUser);
if (results.length > 0) {
response($.map(results, function (item) {
return {label:item.DisplayText,value:item.DisplayText}
}));
}
},
'error':function (err) {
alert(JSON.stringify(err));
}
}
);


}

Working with the response


Unfortunately, the response from the REST call is a string. Typically, all the REST responses in SharePoint will return objects as entities and complex types. In this case the object is a string. At first I thought I was going to have to use the context.parseJSONObject call from CSOM but you can just use JSON.parse and it will create the JSON object array. The response is undocumented. I am hoping Microsoft will expose this within the EDM model in future versions. I already voted for this on user voice UserVoice. Below is an example of what is returned.



Element number 5 is a user and Element 6 represents a SharePoint group. Note the EntityData is different.  The code just uses the DisplayText property to set both the return label and value used by jquery-ui.


What about all those parameters?


The REST API gives you many options to affect the results  a search using the ClientPeoplePickerQueryParameters object. Trying to figure out how these parameters affected searching  was daunting. Many of the parameters only apply to the people picker control but you can use them when implementing them with your own control. I will try to explain each.

AllowEmailAddresses: This is for the people picker control  and allows valid email addresses to be resolved and used as values. It has no effect on the search.

AllowMultipleEntities: This for the people picker control and allows for entering multiple users or groups. It has no effect on the search.


AllUrlZones: Only affects the search if you have set the WebApplicationID. It search across all UrlZones for that particular web application.


MaximumEntitySuggestions: Basically a row limit of how many users or groups are returned.


PrincipalSource: What sources you wish to search. Choices are  All - 15 , Membership Provider - 4 , RoleProvider - 8, UserInfoList - 1  or Windows - 2. These values can be combined.


PrincipalType: Controls the type of entities that are returned in the results. Choices are All - 15, Distribution List - 2 , Security Groups - 4,  SharePoint Groups – 8, User – 1. These values can be combined.


QueryString: The term to search


Required: This is for the people picker control and makes the field required. It has no effect on the search


SharePointGroupID: An integer representing the group ID you want to limit your search to. Only works if you set the Web parameter also which cannot be done via REST.


UrlZone: Limits the search to certain zones within the web application. Can only be used if the UrlZoneSpecified  parameter is set to true. Choices are Custom - 3, Default - 0, Extranet - 4, Internet – 2, IntraNet – 1. These values can be combined.


UrlZoneSpecified: Sets whether you are limiting your search to a particular URL zone in the web application.


Web: If set it works in conjunction with the SharePointGroupID parameter.


WebApplicaitonID: String value representing the Guid of the web application you want to limit your search to.


Easy PeoplePicker Functionality Using REST


Using the SharePoint REST API makes it easy to implement people picking. No need to struggle with the loading of dependent JavaScript files and you get better control over the behavior of the people searching. Finally you can easily use one function to service as many html inputs as you want.

7 comments:

Anonymous said...

Hi Steve,
Thanks for your post. While using above code i got access denied error as $.ajax couldnt do cross domain calls. So modified code to use "SP.RequestExecutor.js".

var scriptbase = hostweburl + "/_layouts/15/";
$.getScript(scriptbase + "SP.RequestExecutor.js", execCrossDomainRequest);

function execCrossDomainRequest() {
var executor = new SP.RequestExecutor(appweburl);
var formDigest;
executor.executeAsync(
{
url: appweburl + "/_api/contextinfo",
method: "POST",
headers: {
'accept': 'application/json;odata=verbose'
},
success: function (data) {
alert('In Success');
formDigest =
JSON.parse(data.body).d.GetContextWebInformation.FormDigestValue;
executor.executeAsync({

url: restSource,
method: 'POST',
body: JSON.stringify({
'queryParams': {
'__metadata': {
'type': 'SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters'
},
'AllowEmailAddresses': true,


After changing also I am getting error as "ExecuteAsync Failure : {"responseAvailable":false,"body":"","headers":null}". please help me in resolving this error.Thanks in advance

Jaydeep Shil said...

I have to search for user within multiple SharePoint custom groups.
with SharePointGroupID property i can set only one group.

Anonymous said...

You are a life saver my friend. Thank you so much. You would think that there's documentation about this service elsewhere.

Krishna Murthy G said...

Steve, could you please share the complete working code

Anonymous said...

With CSOM I am able to get the user's ID for the site collection. In your screenshot, I can see "Style Resource Readers" has a SPGroupID. How would you get REST to return the SPUserID for "Steve Tester"?

Suresh T G said...

Hi,
may i know how to make the group email visible in the search

raymilam said...

man, you saved my day.

Post a Comment