Wednesday, September 17, 2014

Sharing Documents with the SharePoint REST API

Technorati Tags: ,,

Sharing documents is a pretty basic thing in SharePoint. Most  everyone is familiar with the callout action “Share” which enables you to share a document with other people. It is a mainstay of collaboration when working with documents in a team.

However, there is very little documentation on how to do this through the remote API of SharePoint. In this post I will show how you can do this using the REST API and JavaScript. The call has many arguments which can be confusing. The best description I have found for the UpdateDocumentSharingInformation method is here API Description.  Just remember when you are sharing a document you are granting permissions. If you want to use this REST API method in a SharePoint app, then make sure you grant the “Web Manage” permissions in your app manifest. When you click the Share action you are presented a dialog to grant either view or edit permissions to multiple users or roles.

The Code

Below is the REST call using  JavaScript code that shares a document from a SharePoint hosted app.

function shareDocument()
{
var hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
var appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
var restSource = appweburl + "/_api/SP.Sharing.DocumentSharingManager.UpdateDocumentSharingInfo";


$.ajax(
{
'url': restSource,
'method': 'POST',
'data': JSON.stringify({
'resourceAddress': 'http://basesmc15/Shared%20Documents/A1210251607172880165.pdf',
'userRoleAssignments': [{
'__metadata': {
'type': 'SP.Sharing.UserRoleAssignment'
},
'Role': 1,
'UserId': 'Chris Tester'
}],
'validateExistingPermissions': false,
'additiveMode': true,
'sendServerManagedNotification': false,
'customMessage': "Please look at the following document",
'includeAnonymousLinksInNotification': false
}),
'headers': {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose',
'X-RequestDigest': $('#__REQUESTDIGEST').val()
},
'success': function (data) {
var d = data;
},
'error': function (err) {
alert(JSON.stringify(err));
}
}
);

}

The Parameters


ResourceAddress: This is the full URL to the document you want to share


UserRoleAssignments: This an array of users and roles that you want to share the document with. The Role property represents which permission you are assigning. 1 =  View, 2 =  Edit, 3 = Owner, 0 = None. The UserId property can be the name of the user or a role.  For example, if you wanted to share the document with the “Translation Mangers” role and the “Steve Tester” user you would use this JSON:

'userRoleAssignments': [{
'__metadata': {
'type': 'SP.Sharing.UserRoleAssignment'
},
'Role': 1,
'UserId': 'Translation Managers'
},
{
'__metadata': {
'type': 'SP.Sharing.UserRoleAssignment'
},
'Role': 1,
'UserId': 'Steve Tester'
}]

ValidateExistingPermissions: A flag indicating how to honor a requested permission for a user. If this value is "true", SharePoint will not grant the requested permission if a user already has sufficient permissions, and if this value is "false", then SharePoint  will grant the requested permission whether or not a user already has the same or more permissions. This parameter only applies when the additiveMode  parameter is set to true.


AdditiveMode:A flag indicating whether the permission setting uses the additive or strict mode. If this value is "true", the permission setting uses the additive mode, which means that the specified permission will be added to the user’s current list of permissions if it is not there already, and if this value is "false", the permission setting uses the strict mode, which means that the specified permission will replace the user’s current permissions. This parameter is useful when you want to stop sharing a document with a person or group. In this case you would set AdditiveMode to false using the Role = 0.


SendServerManagedNotification: A  flag to indicate whether or not to generate an email notification to each recipient in the userRoleAssignments array after the document update is completed successfully. If this value is "true", then SharePoint will send an email notification if an email server is configured, and if the value is "false", no email notification will be sent.


CustomMessage: A custom message to be sent in the body of the email.


IncludeAnonymousLinksInNotification: A flag that indicates whether or not to include anonymous access links in the email notification to each recipient in the userRoleAssignments array after the document update is completed successfully. If the value is "true", the SharePoint will include an anonymous access link in the email notification, and if the value is "false", no link will be included. This is useful if you are sharing the document with an external user. You must be running this code with full control or as a Site Owner if you want to share the document with external users.


The Results


After calling the above code you will receive a result for every user or role you have shared the document with. The code does not return an error and you must examine the results to determine success. Check the Status property. If this is false typically there will be a message in the Message property explaining the problem. It also tells you whether the user is known. If the user is not known then it is considered an external user.



Resting and Sharing


As you can see there is a lot more to sharing a document versus what is presented in the SharePoint UI. The UpdateDocumentSharingInfo method has many options. You can use this in your custom SharePoint apps to build more robust sharing of documents which could include the option of a custom email message or bulk sharing of documents. This could also be used to stop sharing a document. I have yet to find an easy way to stop sharing a document using the SharePoint UI.

Sunday, August 24, 2014

Using the HttpClient Class with SharePoint 2013 REST API

Technorati Tags: ,

The System.Net.Http.HttpClient class is new in .Net framework 4.5 and was introduced under the ASP.Net Web API. The class and has many methods that support asynchronous programming and  is the best choice for writing client apps that make HTTP requests. Compared to the traditional System.Net.HttpWebRequest class not only does the HttpClient class have more options it also has extension methods such as PostAsJsonAsync and PostAsXmlAsync  methods which are available in System.Net.Http.Formatting assembly shipped with ASP.Net MVC 4 framework. In this post I am going to give you a tip on how to successfully post to the SharePoint REST API using the HttpClient class. There are many examples on how to post to the SharePoint REST API using the HttpWebRequest class but hardly any using the HttpClient class. If you are not careful you would think that the HttpClient class did not work with the SharePoint REST API.

I Keep Getting a 400 Bad Request Message when Posting

Below is a small example on how to use the HttpClient to create a folder in SharePoint 2013. The key to success is setting the Content-Type header correctly when posting. If you have used the REST API you know you must set the Content-Type header to “application/json; odata=verbose”. If you don’t you will get a “400 Bad Request” error. You can use the HttpClient.DefaultRequestHeaders collection to add headers but when trying to add the “Content-Type” header the collection will throw an “InvalidOperationException” with this message “{"Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."}”. So ok I must not be setting the content-type correctly on the HttpContent object. The StringContent class is what you are supposed to use as an argument when calling the HttpClient.PostAsync method. Looking at the StringContent class your first inclination is to use the constructor and give it the json that you want to post. The constructor takes the json, encoding type and media type as arguments. The media type corresponds to the content-type.

StringContent strContent = new StringContent(json, System.Text.Encoding.UTF8, "application/json;odata=verbose");

Unfortunately sending “application/json;odata=verbose” as the media type argument causes a “FormatException” with the message {"The format of value 'application/json;odata=verbose' is invalid."}. If you just use “application/json” you will receive a “400 bad request” error because the “odata=verbose” is missing.  So how do you get around this. First of all you must create the StringContent object with the json as the only argument to the constructor and then set the StringContent.Headers.ContentType property to “application/json;odata=verbose” using the MediaTypeHeaderValue.Parse method.

StringContent strContent = new StringContent(json);               
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");

Mystery solved.

private void CreateFolder(HttpClient client, string digest)
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest);
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");

string json = "{'__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': '/shared documents/folderhttp1'}";
client.DefaultRequestHeaders.Add("ContentLength", json.Length.ToString());
try
{
StringContent strContent = new StringContent(json);
strContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
HttpResponseMessage response = client.PostAsync("_api/web/folders", strContent).Result;

response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync();
}
else
{
var content = response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}

}

HttpClient is Here


HttpClient is a modern HTTP client for .NET. It provides a flexible and extensible API for accessing all things exposed through HTTP. You should use it instead of the HttpWebRequest. You can read more about it here System.Net.Http. Another great source of information when using the HttpClient with SharePoint REST is Dennis RedField’s blog Cloud 2013 or Bust. This blog has an in depth 4 part series on how to use the HttpClient with SharePoint REST. Changing our habits as developers can be a slow process. However, some new APIs can be confusing especially when used against SharePoint 2013. SharePoint 2013 is not fully OData compliant yet and has some quirks, namely content-type checking.  I hope this tip can save you some time.