Document Sharing with the REST API in a SharePoint Add-In

Home » SharePoint Development » Document Sharing with the REST API in a SharePoint Add-In

Document Sharing with the REST API in a SharePoint Add-In

Posted on

Office 365 makes sharing documents with other users extremely easy – just select the item, click ’Share’ and enter an e-mail address or pick a user from the directory – job done. Unfortunately, replicating this behavior in code is not quite so simple. Vesa Juvonen has demonstrated an approach to this using CSOM and there is an associated sample in the Office Dev PnP repo on Github. But what if you need to do this with only HTML and JavaScript, say in a SharePoint Hosted Add-In or mobile application?

Fortunately, there is a REST​ endpoint for this but it is not well documented and the internal workings are a bit obscure. To begin with, you need to send a properly formatted JSON object to the proper REST endpoint in the app web. Unlike most other calls that use the app web as a proxy for resources contained in the host web, calls to the sharing endpoint do not require a parameter for the target (host) web nor does it follow the standard convention of specifying the “/web/“ path in the request URL after “/_api/“. Much like calls to the user profile service or search, this endpoint is directly accessible, which is a big reason why figuring out how to use it is difficult; standardization of RESTful endpoints and related documentation outside of the unified O365 API’s seems to be lagging behind.

In any event, constructing the URL is quite simple compared to other REST calls that require the host web token:

   var reqUrl = appWebUrl + "/_api/SP.Web.ShareObject”;

The above example assumes that your code uses the commonly documented methods for extracting the app web URL from the request parameters (for more information on how to do this, see here). However, knowing the endpoint is only half the battle. The other challenge is figuring out how to properly format the JSON data object that will be POSTed to the specified endpoint. This object has the following parameters:

   url: The full URL of the document to be shared.
   peoplePickerInput: A JSON object containing required user key/value pairs (more on this below)
   roleValue: An numerical value that specifies the desired sharing option (view or edit)
   groupId: An integer which specifies the site group the user should be assigned to.
   propagateAcl: A flag to determine if permissions should be pushed to items with unique permissions.
   sendEmail: A boolean value (‘true’ or ‘false’) used by the system to determine if the target user should be notified of the sharing operation via email.
   includeAnonymousLinkInEmail: A boolean value (‘true’ or ‘false’) which tells the system whether or not to include a link to the document that is reachable by all anonymous users.
   emailSubject: The subject of the notification email.
   emailBody: The body of the notification email.

(The full set of parameters and explanations is described in the following MSDN reference article: https://msdn.microsoft.com/EN-US/library/office/microsoft.sharepoint.client.web.shareobject.aspx)

Of these, the trickiest one to construct is the peoplePickerInputValue. As it is a full JSON object contained within another JSON object, the formatting can be a bit tricky. The entire set of key/value pairs must be contained within square brackets and curly braces and all double quotes escaped and the entire thing assigned to a variable using something like JSON.stringify(). Within the object itself, you must specify such parameters as the full user identifier (claims ID or email address), the type of entity the object represents, the target user’s FQDN, and so forth. When fully defined, the object will look similar to this:

[{\”Key\":\"i:0#.f|membership|user@somedomain.com\",
\"Description\”:\”user@somedomain.com\”,
\”DisplayText\”:\”Test User\”,
\”EntityType\":\"User\",
\"ProviderDisplayName\":\"Tenant\",
\"ProviderName\":\"Tenant\",
\"IsResolved\":true,
\"EntityData\":{\"Title\":\"\",
\"MobilePhone\":\"+1 1234567890\”,
\”Department\":\"\",
\"Email\”:\”user@somedomain\”},
\”MultipleMatches\":[],
\"AutoFillKey\":\"i:0#.f|membership|user@somedomain.com\”,
\”AutoFillDisplayText\”:\”Test User\”,
\”AutoFillSubDisplayText\":\"\",
\"AutoFillTitleText\”:\”user@somedomain.com
\\nTenant\\nuser@somedomain.com\”,
\”DomainText\”:\”somecompany.sharepoint.com\",
\"Resolved\":true,
\"LocalSearchTerm\”:\”user@somedomain.com\”}]



Building this object can be quite challenging, especially if you do not know or cannot get access to all the required information. Fortunately, there is an endpoint you can call from your add-in that returns a properly formatted object you can use in your call to the sharing service. This endpoint resides at the following url:

    http://<AppWeb>/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.
  clientPeoplePickerResolveUser


To make use of it, you will need to pass in an object that includes a set of query parameters, like so:

var restData = JSON.stringify({

   ‘queryParams’: {

      ‘__metadata’: {

         ‘type’: ‘SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters’

      },

      ‘AllowEmailAddresses’: true,

      ‘AllowMultipleEntities’: false,

      ‘AllUrlZones’: false,

      ‘MaximumEntitySuggestions’: 50,

      ‘PrincipalSource’: 15,

      ‘PrincipalType’: 1,

      ‘QueryString’: userId

   }

});



Take note of the “userId” variable – this is the claims identifier or email address of the person the document is being shared with (if the user is not in your Azure AD domain be sure to first enable external sharing in your tenant). POSTing this data to the clientPeoplePickerResolveUser endpoint will return a user object, which can be parsed to obtain the ClientPeoplePickerResolveUser value. This value can then be assigned to the peoplePickerInput parameter.

The entire package then gets POSTed to the app web via the Request Executor. Remember to assign the appropriate value collection to the header value, which includes the “accept”, “content-type” and “X-RequestDigest” parameters. The user interface can then be updated if desired based on the success or failure handlers of the executor. To verify that the sharing operation worked, open the sharing dialog for the document and the user’s ID or email address should be displayed. Below is a full example of a simplified JavaScript function for sharing a specific document (which can also be found in SPRest.Demo repo on GitHub). It requires two inputs, the user email address and the URL of the document, and updates a DIV element on the page with a success or fail message via jQuery.

function shareDocument() {



    try {


        var userId = $("inputEmail").val();


        var docUrl = $(‘#inputFileUrl’).val();


        var executor = new SP.RequestExecutor(appWebUrl);


        var restSource = appWebUrl + "/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerResolveUser";


        var restData = JSON.stringify({


            ‘queryParams’: {


                ‘__metadata’: {


                    ‘type’: ‘SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters’


                },


                ‘AllowEmailAddresses’: true,


                ‘AllowMultipleEntities’: false,


                ‘AllUrlZones’: false,


                ‘MaximumEntitySuggestions’: 50,


                ‘PrincipalSource’: 15,


                ‘PrincipalType’: 1,


                ‘QueryString’: userId


            }


        });


        executor.executeAsync({


            url: restSource,


            method: "POST",


            headers: {


                "accept": "application/json;odata=verbose",


                "content-type": "application/json;odata=verbose",


                "X-RequestDigest": $("#__REQUESTDIGEST").val(),


            },


            body: restData,


            success: function (data) {


                var body = JSON.parse(data.body);


                var results = body.d.ClientPeoplePickerResolveUser;


                if (results.length > 0) {


                    var reqUrl = appWebUrl + "/_api/SP.Web.ShareObject";


                    var executor = new SP.RequestExecutor(appWebUrl);


                    var data = JSON.stringify({


                        "url": docUrl,


                        "peoplePickerInput": ‘[‘ + results + ‘]’,


                        "roleValue": "1073741827",


                        "groupId": 0,


                        "propagateAcl": false,


                        "sendEmail": true,


                        "includeAnonymousLinkInEmail": true,


                        "emailSubject": "Sharing Test",


                        "emailBody": "This is a Sharing Test."


                    });



                    executor.executeAsync({


                        url: reqUrl,


                        method: "POST",


                        headers: {


                            "accept": "application/json;odata=verbose",


                            "content-type": "application/json;odata=verbose",


                            "X-RequestDigest": $("#__REQUESTDIGEST").val(),


                        },


                        body: data,


                        success: function (data) {


                            $("#sharingOutput").html("Sharing succeeded for ‘" + docUrl + "’.").css("color", "green");


                        },


                        error: function (result, code, message) {


                            $("#sharingOutput").html(message).css("color", "red");


                        }


                    });


                }



            },


            error: function (result, code, message) {


                $("#sharingOutput").html(message).css("color", "red");


            }


        });        


    } catch (err) {


        alert(err.message);


    }


}


Eric Shupps Eric Alan Shupps eshupps @eshupps SharePoint Cowboy BinaryWave SmartTrack 
Take the trouble out of troubleshooting.  
Improve service levels and avoid downtime with 

​​​​