Category Archives: Office 365 – Delve

Programmez #190 : Developing with Delve and Office 365 Video REST APIs

Interested in developing applications that use Delve and/or Office 365 Video using REST APIs ?

Take a look to my article in the latest issue of Programmez, a magazine available in French-speaking countries (France, Belgium, Switzerland, Luxemburg, Canada…).

Programmez 190

Retrieving posts from blogs available in Delve with Office 365 REST API

Office 365 evolves every day and among the latest developments, Delve has been enriched with a new feature that allows users to create blog posts in a much simpler way that the old SharePoint blog.

If you access a user profile from Delve, you can see all the posts for this user as you can see on the screenshot below.

Delve - Blog HomePage

But what happens if you want to get an aggregated view of all posts from all users on your tenant ? At this time there’s no feature like that in Delve or even in Office 365, but you can create your own.

In the same way that there’s no feature in Delve to view all posts in a single page, there’s no official API for now to achieve our goal. But in many cases, there’s a solution based on the search engine 😉

Each posts created in Delve use a specific content type. If we submit a search query based on that content type, we’ll be able to retrieve all posts in a simple and fast way.

As a reminder, to submit a search query in Office 365, you can send a POST request to the following URL with a well-formatted HTTP body.

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

As mentioned previously, we want to retrieve only blog posts based on the content type. The body of our search request should be something like :

{
  request = {
    Querytext = "ContentTypeId:0x010100DA3A7E6E3DB34DFF8FDEDE1F4EBAF95D00B0046F2796B8374B872064182468FA7F",
    RowLimit = 10,
    SelectProperties = (
      Title,
      Author,
      Created,
      DefaultEncodingUrl,
      Path
    ),
    StartRow = 0,
    Timeout = 30000,
    TrimDuplicates = True
  }
}

As you can see above, we specified that only few properties (title, author, creation date…) will be returned in the search result.

It’s an important thing to know. All parts of a blog post (subtitle, body, cover image…) can’t be retrieved using a search request because they are not indexed by the search engine.

Indeed, content parts of a post are not stored in a list, like we generally do in SharePoint, but are serialized to a file in JSON. If you want to know what’s the URL of that file, you just have to read the value of the DefaultEncodingUrl for each items in the search result.

The value should be something like the URL you can see below (each post is a file with .pointpub extension) :

https://tenant.sharepoint.com/portals/personal/katiej/pPg/My-new-blog.pointpub

Now that we know what’s the URL, you are able to read the content of each post. At this time, there’s no documentation about the JSON format used in this file so it’s pretty difficult to understand and to use.

If you want a sample of one of that files, take a look below :

{"Version":"1.0","PostType":1,"Title":"My new blog is available","SubTitle":"My First Blog Post","ThumbnailSource":"#315f7e","Author":"anonymous author","ControlData":{"cid144411757865295382":{"Version":"1.0","IsInternalControl":true,"ControlName":"ImageHeaderControl","ControlType":4,"DataContext":{"ImageControlSize":12,"ImageSourceType":3,"ImageSource":"#315f7e","CaptionText":"","Title":"My new blog is available","Subtitle":"My First Blog Post","Author":"Katie Jordan","PublishDate":"Sun Oct 18 2015 08:04:38 GMT+0200 (CEST)","__type":"ImageHeaderControlDataContext"},"__type":"ControlData"},"cid1444117578747883681":{"Version":"1.0","IsInternalControl":true,"ControlName":"RichTextControl","ControlType":0,"DataContext":{"Subtype":1,"Value":"\u003cp\u003e​A new feature is available on Delve and now you are able to create your blog.\u003c/p\u003e","NoDefaultValue":false,"__type":"TextControlDataContext"},"__type":"ControlData"}},"ControlMap":{"Rows":[{"Columns":[{"ControlId":"cid144411757865295382","HasChildren":false}]},{"Columns":[{"HasChildren":false,"ControlId":"cid1444117578747883681"}]}],"GridSize":12,"__type":"ControlMap"},"__type":"PersistedPostModel"}

But if you only want to enumerate posts, with their title, author, creation date and to redirect the user when he clicks on a link, you have all you need to achieve this goal with a simple search query.

To redirect the user to the web page corresponding to the post, you just have to read the value of the Path property. It will be something like that :

https://tenant.sharepoint.com/portals/personal/katiej/_layouts/15/PointPublishing.aspx?storyid=1

Now you are able to retrieve all blog posts available in your Office 365 tenant. But if you want to retrieve posts for a particular user, you can change the search query to add supplementary filters (e.g : use the AuthorOWSUser property).

I just hope that Microsoft will offer in a near future, an API to simplify the way we can retrieve data, especially the content of each post.

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.

How to disable Office Graph and what are the impacts on the REST API

On March 16th, Delve (based on Office Graph) has started rolling out to eligible Office 365 business customers worldwide.

This is a great news for all the users who wants a new way to discover and work with their documents stored in their Office 365 tenant.

But because it’s a brand new way to work, all customers doesn’t necessarily want to offer Delve to all their users for now.

Disable Office Graph on the Office 365 tenant

If you don’t want to use Office Graph in your company, it’s possible to disable it for all the users.

To do it and as a tenant administrator, you have to connect to the SharePoint Admin Center then go the “Settings” section.

In this screen, you just need to check “Don’t allow access to the Office Graph” as you can see in the screenshot below.

Disable Office Graph

Then save your modification and after a short time (from few seconds to few minutes), Delve and Office Graph will be disabled/unusable for all the users of your Office 365 tenant.

Querying the Office Graph with the REST API

Now that we are able to enable/disable the Office Graph at the tenant level, let see how to query it by using the REST API and what happens when the feature was disabled.

For now (Microsoft is working to offer new possibilities/APIs in the future), you have to use the SharePoint search API to query the Office Graph. It’s pretty simple and there’s two different ways to achieve this goal.

My favorite is to POST the query to the search endpoint. The endpoint is accessible from all the SharePoint sites on your tenant.

https://<tenant>.sharepoint.com/_api/search/postquery/
OR
https://<tenant>.sharepoint.com/sites/anotherSiteCollection/_api/search/postquery/

Office Graph queries can be very simple or much more complicated to write, depending on what you want to retrieve. Below, you can see a sample which retrieves the documents (Word, Excel, PowerPoint, PDF and Office 365 Videos) as you can see when you display Delve’s homepage.

{
  "request": {
    "Properties": [
      {
        "Name": "GraphQuery",
        "Value": {
          "QueryPropertyValueTypeIndex": 1,
          "StrVal": "AND(ACTOR(ME,action:1021),ACTOR(ME,OR(action:1021,action:1036,action:1037,action:1039,action:1052)))"
        }
      },
      {
        "Name": "GraphRankingModel",
        "Value": {
          "QueryPropertyValueTypeIndex": 1,
          "StrVal": "action:1021,weight:1,edgeFunc:weight,mergeFunc:max"
        }
      }
    ],
    "QueryTemplate": "({searchterms}) AND ((NOT HideFromDelve:True) AND (FileExtension:doc OR FileExtension:docx OR FileExtension:xls OR FileExtension:xlsx OR FileExtension:ppt OR FileExtension:pptx OR FileExtension:one OR FileExtension:pdf OR ContentTypeId:0x010100F3754F12A9B6490D9622A01FE9D8F012*))",
    "Querytext": "*",
    "RankingModelId": "0c77ded8-c3ef-466d-929d-905670ea1d72",
    "RowLimit": 20,
    "StartRow": 0,
    "Timeout": 30000,
    "TrimDuplicates": true
  }
}

If you want more information about how to build an Office Graph query, please refer to the following MSDN article : https://msdn.microsoft.com/en-us/office/office365/howto/query-Office-graph-using-gql-with-search-rest-api

Execute a query when the Office Graph was disabled

You probably ask yourself : what will happen if we execute the previous query on a tenant from which the Office Graph was disabled ?

As for a lot of things when you use the Office 365 REST API, if you try to use an unavailable feature or if you send malformed data, an exception is thrown and returned to the user.

The Office Graph and the SharePoint search engine work as well. If you try to execute an Office Graph query when the feature is disabled, an exception is thrown. You can see below an example of the server response.

{
  "odata.error": {
    "code": "-1, Microsoft.Office.Server.Search.REST.SearchServiceException",
    "message": {
      "lang": "en-US",
      "value": "Graph queries currently disabled."
    }
  }
}

For now, there’s no REST API to check if the Office Graph feature is enabled or disabled. The only way to check is to execute a query and catch the exception.