Monthly Archives: August 2015

Getting all boards to which a document belongs using the Office Graph

If you use Delve in Office 365 (an extension of Office Graph), you should know what are boards, for logically classify your documents, without moving them to a new physical location (document libraries).

Like every services in Office 365, there’s an API to allow developers to create applications around Office 365. If you want more information about how to query the Office Graph in Office 365, you can refer to the following article at MSDN.

If you read the previous article, you will see that there’s no information in the API documentation, on how to know which boards a document belongs.

To achieve this goal, there’s an undocumented action (ID = 1046) you can use to query the Office Graph. This action is used such as the others :

ACTOR(<ActorId>,action:1046)

If you want to know what are the boards for a given document, the ActorId in the syntax above corresponds to the ID of the document. You can get the ID of a document by retrieving the DocId value from the search engine, by using a simple query or a graph query (e.g. for a document from the personal feed of the current user).

If you query the Office Graph with a syntax such as ACTOR(48202211,action:1046), you will get a search result such as below (it was truncated for focusing on the most important part):

Table = {
  Rows = (
    {
      Cells = (
        {
          Key = DocId,
          Value = 4362767297,
          ValueType = "Edm.Int64"
        },
        {
          Key = Path,
          Value = "TAG://PUBLIC/?NAME=FINANCE",
          ValueType = "Edm.String"
        },
        {
          Key = Title,
          Value = Finance,
          ValueType = "Edm.String"
        },
        {
          Key = Edges,
          Value = "[{\"ActorId\":48202211,\"ObjectId\":4362767297,\"Properties\":{\"Action\":1046,\"Blob\":[123,34,84,97,103,78,97,109,101,34,58,34,70,105,110,97,110,99,101,34,125],\"BlobContent\":\"{\\\"TagName\\\":\\\"Finance\\\"}\",\"ObjectSource\":1,\"Time\":\"2015-08-21T11:34:39.0000000Z\",\"Weight\":1}}]",
          ValueType = "Edm.String"
        }
      )
    },
    {
      Cells = (
        {
          Key = DocId,
          Value = 4362767298,
          ValueType = "Edm.Int64"
        },
        {
          Key = Path,
          Value = "TAG://PUBLIC/?NAME=MARKETING+STRATEGY",
          ValueType = "Edm.String"
        },
        {
          Key = Title,
          Value = "Marketing Strategy",
          ValueType = "Edm.String"
        },
        {
          Key = Edges,
          Value = "[{\"ActorId\":48202211,\"ObjectId\":4362767298,\"Properties\":{\"Action\":1046,\"Blob\":[123,34,84,97,103,78,97,109,101,34,58,34,77,97,114,107,101,116,105,110,103,32,83,116,114,97,116,101,103,121,34,125],\"BlobContent\":\"{\\\"TagName\\\":\\\"Marketing Strategy\\\"}\",\"ObjectSource\":1,\"Time\":\"2015-08-21T11:19:06.0000000Z\",\"Weight\":1}}]",
          ValueType = "Edm.String"
          }
        }
      )
    }
  )
}

The search result above indicates that our document (ActorId = 48202211) was added in two boards called “Finance” and “Marketing Strategy“. Each row in the search result corresponds to a board with its properties (retrieved from those that have been specified in the search query).

Getting boards for multiple documents within the same request

As we have seen, it’s pretty simple to retrieve the boards for a single document but what should I do if I want to retrieve the boards for many documents ?

For performance issue it’s not viable to execute a request for each document. So what’s the solution ?

Office Graph lets you to combine many actors in the same request. So if you execute a request such as the one below (each actor corresponds to a document), you will get as response, a search result with the boards for each document.

OR(ACTOR(48204878,action:1046),ACTOR(48204877,action:1046),ACTOR(48202211,action:1046))

Good, but how can I know which document is associated to which board ?

If you paid attention to the search result, there’s a property called “Edges” for each row. The value of this property is a JSON string that has been serialized.

If we deserialize and format those values, they look like this:

Edges for row #1
[
  {"ActorId":48204878,"ObjectId":4362767297,"Properties":{"Action":1046,"Blob":[123,34,84,97,103,78,97,109,101,34,58,34,70,105,110,97,110,99,101,34,125],"BlobContent":"{\"TagName\":\"Finance\"}","ObjectSource":1,"Time":"2015-08-21T11:18:52.0000000Z","Weight":1}},
  {"ActorId":48202211,"ObjectId":4362767297,"Properties":{"Action":1046,"Blob":[123,34,84,97,103,78,97,109,101,34,58,34,70,105,110,97,110,99,101,34,125],"BlobContent":"{\"TagName\":\"Finance\"}","ObjectSource":1,"Time":"2015-08-21T11:34:39.0000000Z","Weight":1}},
  {"ActorId":48204877,"ObjectId":4362767297,"Properties":{"Action":1046,"Blob":[123,34,84,97,103,78,97,109,101,34,58,34,70,105,110,97,110,99,101,34,125],"BlobContent":"{\"TagName\":\"Finance\"}","ObjectSource":1,"Time":"2015-08-21T20:50:40.0000000Z","Weight":1}}
]

Edges for row #2
[
 {"ActorId":48204877,"ObjectId":4362767298,"Properties":{"Action":1046,"Blob":[123,34,84,97,103,78,97,109,101,34,58,34,77,97,114,107,101,116,105,110,103,32,83,116,114,97,116,101,103,121,34,125],"BlobContent":"{\"TagName\":\"Marketing Strategy\"}","ObjectSource":1,"Time":"2015-08-21T20:50:35.0000000Z","Weight":1}},
 {"ActorId":48202211,"ObjectId":4362767298,"Properties":{"Action":1046,"Blob":[123,34,84,97,103,78,97,109,101,34,58,34,77,97,114,107,101,116,105,110,103,32,83,116,114,97,116,101,103,121,34,125],"BlobContent":"{\"TagName\":\"Marketing Strategy\"}","ObjectSource":1,"Time":"2015-08-21T11:19:06.0000000Z","Weight":1}}
]

As you can see, it’s an array in which each object corresponds to an actor with some properties.

The important values for each object corresponds to :

  • ActorId ==> The ID of our document
  • ObjectId ==> The ID of the board
  • TagName ==> The name of the board
  • Time ==> When the document was added to the board

In this example in which we queried the Office Graph for three documents, two of them (ActorId = 48204877 and ActorId = 48202211) were added in two boards (“Finance” and “Marketing Strategy“). The third document (ActorId = 48204878) was only added in the “Finance” board.

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).