Using Alfresco associations in CMIS

CMIS is a standard specification designed to be used as client API for different ECM platforms. Alfresco provides great support for this standard.

However, when managing associations (named relationships in CMIS), there are some limitations to consider:

  • Associations cannot be created when creating the node, they must be created after
  • Associations are not available in CMIS QL, as cmis:relationship is not queryable

A sample project is available at cmis-associations-alfresco in order to describe these operations.

This project includes an Alfresco Content Model with an association named cmisassoc:related and an additional property named cmisassoc:relatedRef to store a copy of these associations. This copy property can be used from CMIS QL to get the related documents.

The project also provides a sample CMIS Client based in Spring Boot for testing.

Using CMIS Browser protocol

CMIS (Content Management Interoperability Services) is an Oasis Standard to work with one or more Content Management repositories.

The API is exposed using three different bindings for clients:

Additionally, several client libraries are provided in Apache Chemistry web page, like Java, Python, PHP, .NET, Objective-C and JavaScript.

In this blog post, RESTful Browser binding operations for an Alfresco CMIS Repository are described.

Base URL and Authentication

For Alfresco 4.2+, the base URL for CMIS Browser binding is:

http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser

It can be used HTTP Basic Auth to obtain repositories information using this base URL:

$ curl -X GET \
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser \
     -H 'Authorization: Basic YWRtaW46YWRtaW4='
{
   "-default-":{
      "repositoryId":"-default-",
      "repositoryName":"",
      "repositoryDescription":"",
      "vendorName":"Alfresco",
      "productName":"Alfresco Enterprise",
      "productVersion":"6.1.0 (3 r0f0034ee-b79)",
      "rootFolderId":"b1ee4176-6712-468c-89b0-dd0352e93450",
      "latestChangeLogToken":null,
      "cmisVersionSupported":"1.1",
      …
   }
}

Navigation

Repository can be browsed by using Folder and Document names in the URL. For instance, to have access to “Shared” folder in Repository root, it can be used following sentence:

$ curl -X GET \
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root/Shared \
-H 'Authorization: Basic YWRtaW46YWRtaW4='
{
   "objects":[
      {
         "object":{
            "properties":{
               "cmis:name":{
                  "value":"New Folder"
                  }
            }
         }
      },
      {
         "object":{
            "properties":{
               "cmis:name":{
                  "value":"Sample-Document.docx"
               }
            }
         }
      },
      {
         "object":{
            "properties":{
               "cmis:name":{
                  "value":"test.txt"
                  }
            }
         }
      }
   ],
   "hasMoreItems":false,
   "numItems":3
}

Children list is returned as result, as this is the default Children Object for a Folder Object. If we were accessing to a different type of object, following elements are returned as result:

cmis:document        content
cmis:folder          children
cmis:relationship    object
cmis:policy          object
cmis:item            object

Objects

When accessing an object, different detail can be specified by using cmisselector parameter in an HTTP GET request: children, parents, object, properties, content, renditions, version, relationships, policies, acl

Accessing to document properties, can be expressed with the following URL:

$ curl -X GET \
'http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root/Shared/test.txt?cmisselector=properties' \
-H 'Authorization: Basic YWRtaW46YWRtaW4=' \

Actions

In order to perform operations in a Repository node, cmisaction parameter in an HTTP POST request must be used: createDocument, createFolder, createRelationship, createPolicy, createItem, query, createType, deleteType, delete, deleteTree, deleteContent, checkOut, checkIn, update

A new document of type cmis:document and name test.txt (uploading content from local file stored at /tmp/test.txt) can be created by using createDocument action.

$ curl -X POST \
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root/Shared \
-H 'Authorization: Basic YWRtaW46YWRtaW4=' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F cmisaction=createDocument \
-F 'propertyId[0]=cmis:objectTypeId' \
-F 'propertyValue[0]=cmis:document' \
-F 'propertyId[1]=cmis:name' \
-F 'propertyValue[1]=test.txt' \
-F file=@/tmp/test.txt

This document can be removed using delete action.

$ curl -X POST \
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser/root/Shared/test.txt \
-H 'Authorization: Basic YWRtaW46YWRtaW4=' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F cmisaction=delete

And even queries can be also launched by using the query action.

$ curl -X POST \
http://localhost:8080/alfresco/api/-default-/public/cmis/versions/1.1/browser \
-H 'Authorization: Basic YWRtaW46YWRtaW4=' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F cmisaction=query \
-F 'statement=select * from cmis:document where cmis:name like '\''test.txt'\'''

It’s recommended to use in every HTTP request the param succinct=true to obtain a smaller payload.

Additional samples

Additional CMIS Browser REST API invocations are available at GitHub as a Postman Collection:

https://gist.github.com/aborroy/3f1f2360b0e85067675643aa648a8112

Using secondary types (aspects) as cmis:items in Alfresco CMIS queries

CMIS Standard 1.1 defines a cmis:item object type as:

A new top level data model type that is an extension point for repositories that need to expose any other object types via CMIS that do not fit the model’s definition for document, folder, relationship or policy.

But it also stays that:

Please note that the following text and the BNF grammar are inconsistent. The text allows the use of secondary object types in FROM clauses and the BNF grammar does not. Because the BNF grammar is the formal description of the query language, it takes precedence over the text. That is, secondary object types MUST NOT be used in FROM clauses.

The next version of the CMIS specification will correct the BNF grammar and allow the use of secondary object types in FROM clauses.

As there will be no next version of the CMIS specification, according to specifications SQL Queries using secondary types (called aspects in Alfresco) should not be supported by CMIS implementations.

However in Alfresco this feature is implemented, so you can use aspects as cmis:item in your queries in the same way you use an object type.

For instance, to select every node having cm:titled aspect you can write:

SELECT * FROM cm:titled

I’ve been using this syntax for years (unknowing that it was outside the standard) and it works without issues. I guess that is safe to use this also in the future, so I’ll keep on using it.

Thanks to Axel Faust for letting me know about this.

Further reading

Cool things you can do in Alfresco with cmis:item support by Jeff Potts