Category Archives: Office 365 – SharePoint

Performing a search request with SharePoint REST API throws a Microsoft.SharePoint.Client.UnknownError

Most of Office 365 REST APIs use the OData v4.0 protocol for exchanging and formatting data between the client and the server.

When you develop an application that uses those APIs, you need to add some HTTP headers in all your requests to let the server know that you want to use this protocol version and to specify the desired verbosity of returned metadata as you can see below :

Accept: application/json;odata.metadata=minimal
OData-Version: 4.0

But if you use these HTTP headers with the SharePoint API to perform a search by calling…

POST https://tenant.sharepoint.com/_api/Search/PostQuery

… then you will receive the following error message :

{
  "error": {
    "code":"-1,
    Microsoft.SharePoint.Client.UnknownError",
    "message":"Unknown Error"
  }
}

You could be surprised because if you try to get information about a site collection by calling…

https://tenant.sharepoint.com/_api/Site

… with the same HTTP headers, it works without any issues.

It can look weird but in fact it’s normal.

If you ever call the following URL to retrieve the OData manifest for the SharePoint API…

https://tenant.sharepoint.com/_api/$metadata

… you can see that the API only supports OData v3.0 (cf. MaxDataServiceVersion).

<edmx:Edmx Version="1.0">
  <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0">
    ...
  </edmx:DataServices>
</edmx:Edmx>

So even if a lot of features in the SharePoint REST API are working with the OData v4.0 headers, you should better use the OData v3.0 headers such as below to avoid such issues :

Accept: application/json;odata.metadata=minimal
DataServiceVersion: 3.0

Share your SharePoint sites with external users using CSOM

Yesterday on the Office 365 Yammer Network, someone asked if it was possible to add external users to a group using CSOM.

After a quick research, I found that it was a recurrent question and there’s no clear answer to that question. So I decided to investigate and I found the answer 🙂

In CSOM, there’s a namespace called Microsoft.SharePoint.Client.Sharing in which there are two useful classes called WebSharingManager and DocumentSharingManager.

These two classes are used to share a site or a document with users (internal or external).

In my case, I want to share a site with an external user so I need to use WebSharingManager.

This class has a method called UpdateWebSharingInformation with the following signature :

public static IList<UserSharingResult> UpdateWebSharingInformation(
  ClientRuntimeContext context,
  Web web,
  IList<UserRoleAssignment> userRoleAssignments,
  bool sendServerManagedNotification,
  string customMessage,
  bool additivePermission,
  bool allowExternalSharing
)

As you can see, there are many parameters which are very easy to understand but the important parts are userRoleAssignments and allowExternalSharing.

The first is a collection of users with whom you want to share the resource. The second is to enable external sharing if the users are not employed by your company.

How to use it in practice ? It’s very easy.

using(var password = new SecureString())
{
  foreach(var c in "myPassword".ToCharArray())
    password.AppendChar(c);

  using(var ctx = new ClientContext("https://tenant.sharepoint.com/"))
  {
    ctx.Credentials = new SharePointOnlineCredentials("user@tenant.onmicrosoft.com", password);

    var users = new List<UserRoleAssignment>();
    users.Add(new UserRoleAssignment()
    {
      UserId = "username@externaldomain.com",
      Role = Role.View
    });

    WebSharingManager.UpdateWebSharingInformation(ctx, ctx.Web, users, true, null, true, true);
    ctx.ExecuteQuery();
  }
}

When you execute the code above, the external user is invited as a viewer in the site associated to the CSOM context. He will receive an email to access to the site.

If you want to know if the invitation was sent successfully and if the user has accepted (or not), you just have to go to Access requests and invitations through the site settings.

External Sharing / Pending Requests

If you want more information about WebSharingManager and DocumentSharingManager, just go to the official documentation on MSDN.

How to know what file types are supported to generate previews on Office 365 ?

In our previous articles (here and here) we have seen how to generate a preview for a document stored in SharePoint / Office 365.

When you develop applications around Office 365 and because you can store potentially (there are some exceptions) any types of documents in a SharePoint site, it could be interested to know what file types are supported to generate a preview for those documents.

There’s no official documentation on how to achieve this operation but you have to know that it’s possible using the same handler as that described in our previous articles (GetPreview.ashx).

Indeed, that handler has some hidden features and one of them is the ability to get supported file types. To do that, you just have to call the following URL (don’t forget to pass a valid authentication token in HTTP headers, as you do with REST APIs) :

https://<tenant>.sharepoint.com/_layouts/15/GetPreview.ashx?Action=supportedtypes

When you call that URL, the server return a JSON response such as the following :

{
  "DocumentTypes":[
    {"Always":false,"FileExtension":".wmv","MultiPreview":false},
    {"Always":false,"FileExtension":".3gp","MultiPreview":false},
    {"Always":false,"FileExtension":".3g2","MultiPreview":false},
    {"Always":false,"FileExtension":".3gp2","MultiPreview":false},
    {"Always":false,"FileExtension":".asf","MultiPreview":false},
    {"Always":false,"FileExtension":".mts","MultiPreview":false},
    {"Always":false,"FileExtension":".m2ts","MultiPreview":false},
    {"Always":false,"FileExtension":".avi","MultiPreview":false},
    {"Always":false,"FileExtension":".mod","MultiPreview":false},
    {"Always":false,"FileExtension":".dv","MultiPreview":false},
    {"Always":false,"FileExtension":".ts","MultiPreview":false},
    {"Always":false,"FileExtension":".vob","MultiPreview":false},
    {"Always":false,"FileExtension":".xesc","MultiPreview":false},
    {"Always":false,"FileExtension":".mp4","MultiPreview":false},
    {"Always":false,"FileExtension":".mpeg","MultiPreview":false},
    {"Always":false,"FileExtension":".mpg","MultiPreview":false},
    {"Always":false,"FileExtension":".m2v","MultiPreview":false},
    {"Always":false,"FileExtension":".ismv","MultiPreview":false},
    {"Always":false,"FileExtension":".mov","MultiPreview":false},
    {"Always":true,"FileExtension":".docm","MultiPreview":false},
    {"Always":true,"FileExtension":".docx","MultiPreview":false},
    {"Always":true,"FileExtension":".dotx","MultiPreview":false},
    {"Always":true,"FileExtension":".dotm","MultiPreview":false},
    {"Always":true,"FileExtension":".bmp","MultiPreview":false},
    {"Always":true,"FileExtension":".jpg","MultiPreview":false},
    {"Always":true,"FileExtension":".jpeg","MultiPreview":false},
    {"Always":true,"FileExtension":".tiff","MultiPreview":false},
    {"Always":true,"FileExtension":".tif","MultiPreview":false},
    {"Always":true,"FileExtension":".png","MultiPreview":false},
    {"Always":true,"FileExtension":".gif","MultiPreview":false},
    {"Always":true,"FileExtension":".emf","MultiPreview":false},
    {"Always":true,"FileExtension":".wmf","MultiPreview":false},
    {"Always":false,"FileExtension":".pdf","MultiPreview":false},
    {"Always":true,"FileExtension":".pptm","MultiPreview":true},
    {"Always":true,"FileExtension":".pptx","MultiPreview":true},
    {"Always":true,"FileExtension":".potm","MultiPreview":true},
    {"Always":true,"FileExtension":".potx","MultiPreview":true},
    {"Always":true,"FileExtension":".ppsm","MultiPreview":true},
    {"Always":true,"FileExtension":".ppsx","MultiPreview":true},
    {"Always":false,"FileExtension":".xlsm","MultiPreview":false},
    {"Always":false,"FileExtension":".xlsx","MultiPreview":false}
  ]
}

You get an array of document types and for each item, you are able to know :

  • FileExtension => Does it really need to be explained ? 😉
  • Always => Does the handler is always able to generate a preview or can it fail (e.g. because the file contains unsupported features)
  • MultiPreview => Does the handler is able to generate multiple previews for the same document (e.g. slides on PowerPoint presentations)

As you can see, there’s only a few document types that are supported by Office 365 to generate previews. Now you can improve your applications to not try to generate previews for unsupported types 😉

Create time-limited share link for a SharePoint document using REST API

If you use SharePoint to store your documents, you necessarily needed one day to share a document with an external user to your organization.

SharePoint, like other solutions such as OneDrive, DropBox, Box, GoogleDocs… gives you the capability to generate guest links on files stored in document libraries.

To be able to generate those kind of links, you must enable the correct option at the tenant level as you can see on the screenshot below.

External Sharing - Settings

When the option is enabled, you generate the link from the share dialog box on a document.

External Sharing - Guest Link

It’s very useful but as you can see, there’s no option to limit the time for which the link is available. When you want to disable the link, you have to go back to the dialog box of the document an select the “Disable” option. If you do this on hundreds or thousands of documents, it will be unmanageable very fast.

REST API to our rescue

Fortunately, as developers, there’s often a solution that we can use to cover our needs.

In our case, if you look into the REST API (but it’s also available with CSOM), on a SP.File object, there’s a method called GetPreAuthorizedAccessUrl which allows you to generate a time-limited link.

There’s many possibilities to get a SP.File object to then call the method, so let’s take an example using the GetFileByServerRelativeUrl from an SP.Web :

https://tenant.sharepoint.com/_api/Web/GetFileByServerRelativeUrl('/Shared%20Documents/2013%20Contoso%20products.pdf')/GetPreAuthorizedAccessUrl(24)

As you can see above, we retrieve the SP.File object associated to a file stored in a library, then we generate the link which will be available for one day (24 hours). The response returned by the server is something like :

https://tenant.sharepoint.com/_layouts/15/download.aspx?guestaccesstoken=lsRrsejmU9HlzY0Kw87A9NFu4yeDgMPusDBGF%2frz6AU%3d&docid=0588b11e4a047423e8667ee0ed9d67bf9&expiration=8%2f18%2f2015+9%3a14%3a34+AM&userid=28&authurl=True

Now that you have a link which doesn’t require authentication to access/download the file, you just have to send it to your external users.

After one day, if they try to use the same link, they will receive an error message.

As we have seen in this article, SharePoint has all necessary things to generate time-limited links to share documents.

Unfortunately, there’s no option (at this time) in the UI to use this capability, so it’s up to you to develop it. 😦

Determine which languages are activated on a SharePoint site with REST API

If you develop mobile applications which need to be multilingual, it will happen sooner or later that you wanted to know which languages are activated on your SharePoint sites.

To retrieve information about your SharePoint sites, your first instinct will be to use the REST API. To achieve this goal you typically call URLs such as :

https://contoso.sharepoint.com/_api/Web
https://contoso.sharepoint.com/sites/HR/_api/Web

By default when you call those URLs, not all properties available for the site are returned in the server response. Only a subset (called the default scalar property set) is included in the response as you can see in the sample below :

{
    AllowRssFeeds = 1;
    AlternateCssUrl = "";
    AppInstanceId = "00000000-0000-0000-0000-000000000000";
    Configuration = 0;
    Created = "2015-06-05T15:12:37.89";
    CustomMasterUrl = "/_catalogs/masterpage/seattle.master";
    Description = "A new description for the Contoso Team Site";
    DocumentLibraryCalloutOfficeWebAppPreviewersDisabled = 0;
    EnableMinimalDownload = 1;
    Id = "7e75f9bc-db75-4425-b934-86bdf913f9c5";
    IsMultilingual = 1;
    Language = 1033;
    LastItemModifiedDate = "2015-08-05T17:11:31Z";
    MasterUrl = "/_catalogs/masterpage/seattle.master";
    OverwriteTranslationsOnChange = 0;
    QuickLaunchEnabled = 1;
    RecycleBinEnabled = 1;
    ServerRelativeUrl = "/";
    SiteLogoUrl = "<null>";
    SyndicationEnabled = 1;
    Title = "Contoso Team Site";
    TreeViewEnabled = 0;
    UIVersion = 15;
    UIVersionConfigurationEnabled = 0;
    Url = "https://contoso.sharepoint.com";
    WebTemplate = STS;
    "odata.editLink" = Web;
    "odata.id" = "https://contoso.sharepoint.com/_api/Web";
    "odata.metadata" = "https://contoso.sharepoint.com/_api/$metadata#SP.ApiData.Webs/@Element";
    "odata.type" = "SP.Web";
}

As you can see in the default scalar property set, we are able to know if the current site is multilingual or not (IsMultilingual = 1), but we don’t know which languages have been activated.

To retrieve the value we want, we need to modify our call to the REST API so that we have to include additional parameters in the querystring to specify which properties must be included in the server response.

https://contoso.sharepoint.com/_api/Web?$select=Title,Url,IsMultilingual,SupportedUILanguageIds

When we execute that request, the server return the following response, and we now are able to know which languages (SupportedUILanguageIds) are used by our SharePoint site.

{
    IsMultilingual = 1;
    SupportedUILanguageIds =     (
        1033,
        1036
    );
    Title = "Contoso Team Site";
    Url = "https://contoso.sharepoint.com";
    "odata.editLink" = Web;
    "odata.id" = "https://contoso.sharepoint.com/_api/Web";
    "odata.metadata" = "https://contoso.sharepoint.com/_api/$metadata#SP.ApiData.Webs/@Element&$select=Title,Url,IsMultilingual,SupportedUILanguageIds";
    "odata.type" = "SP.Web";
}

Note that although it’s very useful to be able to select which properties will be included in the server response, if you want to include all the default properties plus a new one (SupportedUILanguageIds in our case), you have to put the entire list in the querystring.

Many types in SharePoint REST API doesn’t include all available properties in their default scalar property set. If you need to know which properties are included and which are not, you can refer to the documentation (e.g. https://msdn.microsoft.com/en-us/library/dd928431%28v=office.12%29.aspx).

Generate thumbnail of a document stored in SharePoint from its URL

In a previous article, we have seen how to generate a document thumbnail from its metadata (siteId, webId, uniqueId, docId…) retrieved from the search engine.

With these metadata, you just have to build and call an URL such as the following :

https://tenant.sharepoint.com/_layouts/15/getpreview.ashx?guidFile=<GUID>&guidSite=<GUID>&guidWeb=<GUID>&docid=<Int>&metadatatoken=<String>

Although easy to implement, this solution is not convenient every time, especially when you don’t use the search engine to retrieve data.

For example, when you use the Video REST API or the SharePoint REST API to manipulate list items, you don’t retrieve some of the required properties to generate the preview as described in our previous article.

One solution should be to query the search engine for the associated file to retrieve required metadata, then build and call the URL to generate the preview. This solution works very well but it’s pretty inefficient because you have to make 2 requests for each thumbnail.

A better solution based on the same HTTP Handler

Last week, I was working on different things around Office 365 and I discovered a better solution based on the same HTTP Handler.

It’s possible, rather than passing many parameters (siteId, webId, docId…), to pass only one parameter called ‘path’.

This parameter expects a value equal to the URL of the document/file for which you want to generate a thumbnail. To generate a preview, the URL should look like this :

https://tenant.sharepoint.com/portals/hub/_layouts/15/getpreview.ashx?path=https%3A%2F%2Ftenant.sharepoint.com%2Fportals%2Fcommunity%2FpVid%2FBiking%2520to%2520Work.mp4

In the sample above which generates a thumbnail for a video stored in an Office 365 video channel), you just have to encode the path of the file, and add it to the querystring for the ‘path‘ parameter.

I have tested with many file types (Word, Excel, PowerPoint, MP4…), stored in different containers (document libraries, video channels…) and it works for each of them.

For information, this solution is used by Delve to generate thumbnails for video files stored in Office 365 Video 😉

Delve - HomePage

Make your life easier with automatic storage management on SharePoint site collections

When you create a SharePoint site collection on your Office 365 tenant, by default you have to specify a storage quota which defines what’s the maximum amount of space you want to allocate to that site collection.

The quota is decreased from the storage pool assigned to your Office 365 tenant, depending on the number of licenses you purchased. By default the amount of available space is equal to 10 GB + 500 MB per user (but you can purchase additional storage space if necessary).

It’s pretty simple to use but there’s an issue with this model. Even if there’s no content stored in the site collection, the quota allocated is decreased from your storage pool. When the amount of available space is equal to zero, you won’t be able to create new site collections.

When you are an administrator of your Office 365 tenant, you are able to see all the site collections and the associated quotas from the SharePoint administration site (https://mytenant-admin.sharepoint.com/_layouts/15/online/SiteCollections.aspx) as you can see below.

Site Collection Storage Management - Before activation

But few months ago, Microsoft announced they were working on a new feature that would simplify the administration of storage space. That feature is available since few days on my tenants so let’s see how to activate it.

To achieve that goal, you have to be an Office 365 administrator and you have to go to the SharePoint admin center. On this site, just select the settings option from the left menu.

If the option is available on your tenant (the deployment on all Office 365 tenants may take some weeks), you should see a new option called Site Collection Storage Management as you can see on the following screenshot.

Site Collection Storage Management - Activation

To activate this new feature, you just need to select the Automatic option (by default it should be on Manual mode) and save your modifications.

If you go back on the screen which list all the site collections created on your Office 365 tenant, you should see that the columns named “storage limit” and “percent used” have disappeared.

Site Collection Storage Management - After activation

Now SharePoint will automatically manage the storage allocation, depending on the needs of each site collection. If your prefer to manage yourself the allocation, you can switch back to the manual mode to define quotas for each site collection.