Wednesday, December 3, 2014

Upload file in SharePoint online document library (folder) with REST and active authentication.

In this post I'll explain you what I did to upload a local file into a SharePoint Online Office 365 document library. The second step in this example is providing the new document with a proper value for the title meta data field.
This example is a console application written in C#, not using the Microsoft.SharePoint.Client.dll but only making calls to REST endpoints.
A CRM developer asked me to write this piece of code so he could implement this logic into a custom CRM module, that module had to copy a file from CRM online to SharePoint online.

When the application is launched the console will ask you for some parameters:
  • path of the file to upload
  • title for the new document 
  • document library name
  • folder name (in this example, I expect a folder)
  • site collection URL (in this example, sub sites are not taken into account)
  • O365 user name
  • password
As you see, the application will ask for a user name and password. These credentials are used to authenticate the application with SharePoint. I've used the authentication mechanism build by Wictor Wilén. If you're interested in this authentication mechanism you definitely should read his post.

This console app is just an example on how we can upload a document. The SharePoint document library and the folder must exist, the code won't create them for you. If they don't exist, the code will throw a 404 exception.

If everything goes well, you should see something like this:

Where the magic happens

The download link is available at the end of this article. Download and adapt for your needs.

As explained earlier the application exists of 3 blocks
  1. authentication -> get security token by calling STS of Office 365. This security token is sent to SharePoint, if SharePoint accepts it will return 2 cookies. Those cookies we have to include in each (REST) request we do to SharePoint.
  2. Reading the local file as a byte array and uploading the file in the correct folder.
  3. When the upload is succesfull, we give the Title metadata field a proper value.

Reading and uploading the file with REST (c#)

Following code snippets are only there for explaination reasons, download the full example to get a working application.

When authentication is succesfull and we have the 2 required cookies in our container, I have to obtain the formdigest, this digest is mandatory if your REST call is writing data to SharePoint. We will inlcude this formdigest as property X-RequestDigest in the header of the request. The binaryStringRequestBody property is the second one we add in the header. This property is mandatory because we will upload a document as a byte array.
When preparations are done we can actually do the webrequest by calling HttpHelper.SendODataJsonRequest. After including the cookies, this function will call SendHttpRequest() where the actual request is done. Properties of the request:

  • request.Method = "POST"
  • request.Accept = "application/json;odata=verbose"
  • request.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; //must be there, otherwise http 403 error
  • request.AllowAutoRedirect = false; // This is key, otherwise it will redirect to failed login SP page
  • request.Headers
    • binaryStringRequestBody: true
    • X-RequestDigest: [value of formDigist]
  • request.ContentType = "application/json;odata=verbose"
  • request.ContentLength = requestContent.Length;
  • request.GetRequestStream() -> read byte array of file.

Changing Title metadata field of the fresh document.

Second step is to give the new document a proper title. In fact the process is very similar to the upload of the document. Here a list of the differences:

  • body: instead of processing the byte array of the document, now the body of the request will contain a string in which we define the changes to the metadata.
  • headers: since we are doing again a POST request, the X-RequestDigest is again required for security reasons. 2 other properties are required as well:
    • X-HTTP-Method = MERGE
    • IF-MATCH = *
The MERGE method updates only the properties of the entity that you specify.

Here you can download the Visual Studio Project.

**UPDATE: Since System.Web.Extensions is not available in a CRM sandboxed solution, json deserialization is done with System.Runtime.Serialization.Json. Thanks to Kris for helping me out.

Wednesday, October 8, 2014

Installation App for SharePoint On-prem

During the installation of a provider hosted app on a SharePoint on-prem environment I encountered following error:
The remote event receiver callout failed. Details: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate,NTLM'
There was a remote event receiver binded to the AppInstalled event, the callout to this webservice failed.
This error seems indeed correct. The web application, hosted in IIS, was configured for Windows Authentication only.
So, this means that SharePoint calls this web service anonymously? If someone has a better explanation for this, please let me know.

I decided to open the website in IIS, and I changed the authentication for the folder that contained the webservices. Only for this folder I enabled Anonymous and disabled Windows Authentication. This works...

If someone has a better solution for this issue, please leave a comment.

Create Azure Service Bus for debugging SharePoint apps.

If you install Office Developer Tools for Visual Studio 2012, you can debug apps for SharePoint. By using the Microsoft Azure Service Bus, these tools communicate with the same Windows Communications Foundation (WCF) service that remote event handlers (remote event receivers and app event receivers) use. By taking this approach, you avoid network boundary issues between the cloud app and the local web app. This lets you debug remote event receivers in the cloud app. See Developing apps for SharePoint on a remote system.
In fact, what you need is an Azure ACS Service bus. A while ago we could copy the ACS connection directly in the Azure web interface. Now, when you create a new Service Bus via the Azure web interface, only the SAS connectionstring is available.
Solution: create service bus via Azure Powershell:

1. Open Azure Powershell

2. I got this exception:

AuthenticationFailed: A security token exception occured for the received JWT token.

...because I logged on with a Microsoft Account. You can only create a new service bus with an Active Directory account. This means if you have multiple accounts available and you get the connection dialog, you should go for the directory account. If you don't have an Active Directory account, you should go to Azure, there you can create one (and bind it to a subscription).

 After I logged on with the correct account, the creation went well.

Friday, August 29, 2014

Adapt view of a ListViewWebPart with CSOM.

In a previous post, I explained how we can easily provision a SharePoint site with the client side object model. In this post I will explain how you can change the view of the ListViewWebPart with CSOM.
Probably this is something you want to do all the time because when you add a ListViewWebPart to a page, in the backgound SharePoint will generate a new hidden view in the views collection of the corresponding list.
Unfortunately, this new view isn't a copy of the current default view of the list...

As always, to work fast, I'm using the OfficeAMS extension methods. Since my last post, things have changed for OfficeAMS, as they say themselves: they've grown up.

Friday, August 22, 2014

SharePoint Online header region background color issue

This week our production tenant (and my development tenant as well) got updated with the new 'higher' header region.

Strange enough, when hovering the flyout menus. The background was black, only the selected item was visible.

To fix this issue, you just have to switch themes: Site Settings - Change the look - select appropriate theme - Click 'try it out' - Click 'Yes, keep it'.

new green theme: issue fixed

...and back to blue:

Monday, July 21, 2014

Add ListViewWebPart to wiki page with CSOM

Here is how you can add very easily a ListViewWebPart to a SharePoint wiki page. I've used this code in a provider hosted app for SharePoint online, but of course this will work on-premise as well.
In this example I'm using again the powerfull extensions methods of OfficeAMS.

In SharePoint 2013 list view web parts are still the same xsltlistviewwebparts that we are used to work with. The only difference in this new version is that we have the ability to overwrite the rendering on the client side by making use of JS Link.

A little bit of background information: a remote event receiver is binded to a list, OnItemAdded, I'm creating a new SharePoint site which I'm provisioning with CSOM. I need a document library, a page and a ListViewWebPart of the document library on the wiki page.

You'll have noticed that the WebPartXml property of the WebPartEntity contains a reference to my globals class. In this class, I've created an xml snippet which I can reuse if I want to add a xsltlistviewwebpart to a page. Using this snippet requires 2 params, the List.Id and a title for the web part. Feel free to adapt this snippet for your requirements (toolbar, JS Link reference...)

In a next post, I'll show you how to adapt the view of the webpart.

Friday, July 11, 2014

Register remote event receiver (RER) with CSOM

I'm currently working on a provider hosted SharePoint 2013 app. The aim is to build a self provisioning app by making use of the client side object model  (csom).

In an old fashion way, provisioning of site columns, content types, lists... was done with declarative coding. Unfortunately, declarative host web provisioning isn't possible. Luckily for us developers, the OfficeAMS contributors are working hard on building nice extension methods to speed up SharePoint development. 

in the following example I'm provisioning the host web during the AppInstalled event.
  • creating a new list
  • binding the remote event receiver to the newly created list
The remote event receiver is a .svc service hosted on the remote web of the provider hosted app. In my example I'm using Azure.

In fact, binding the RER isn't rocket science, but since the ReceiverUrl property is set to the absolute address of the .svc we won't be able to debug this thing. If we want to debug a remote event receiver hosted on Azure we have to make use of an azure service bus.

This is why I'm using the #if DEBUG directives of visual studio. If in debug mode, I'm overwriting the absolute .svc url in the following format: 

This raises the question, what are the computer, account and guid parameters in this URL? I figured out these values by opening the .debugapp package that is generated by visual studio during debugging.

While debugging you can find this package in the debug folder of your project.
Extract this .debugapp package and open the AppManifest.xml file in a text editor.
The InstalledEventEndpoint contains all the values your are looking for.

Thursday, July 3, 2014

Copy a file across site collections with REST

I had to give the users the possibility to copy a file from the SharePoint search results to a document library in another site collection. This requirement resulted in the following custom display template: when clicking the 'Copy to other site collection' button, a div is expanded and shows a text box to provide a new filename and 2 radio buttons to select the target document library.

All the code is based on the example from Mikael Svenson. Since he is copying a file from appweb to hostweb he is using the SP.REquestExector javascript library. When I first read his article I thought this technique wasn't usable for my problem because I was going to copy files within the same domain.

Altough the SP.RequestExecutor library is a cross-domain javascript libary, this library is perfectly usable to use within the same domain. And it is fixed, so the patching described by Mikael isn't required anymore :)

This example was built for SharePoint 2013 online but works on premise as well.