This section is all about managing folders and files.
In this section we will cover how to manage folders and files, also referred to as nodes, with the Alfresco ReST API.
After walking through this section you should have a good understanding of how to list contents of a folder, create a folder, upload a file, set metadata for a folder or file, update a file, delete folders and files, and much more.
List contents of a folder
Listing the contents of a folder in the repository is really useful, here we walk through several examples of how to do that.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/listNodeChildren
See also: Filter contents of a folder
Listing the folders and files in the top folder of the repository is often something you often need to do to get an idea
of what’s stored in the Repository. The top folder of the Repository is called /Company Home
and referred to as root.
The following URL is used to list children of the root folder and we pass in the authentication token:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2764 100 2764 0 0 38388 0 --:--:-- --:--:-- --:--:-- 38388
{
"list": {
"pagination": {
"count": 7,
"hasMoreItems": false,
"totalItems": 7,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2018-07-04T12:57:32.509+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:55.796+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Data Dictionary",
"id": "ec5e2242-e2cf-4fca-977c-f980efa289aa",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:32.799+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2019-01-14T14:59:30.806+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Guest Home",
"id": "18f06331-a7e2-450b-9951-4d27bc2597f9",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:32.874+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:32.874+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Imap Attachments",
"id": "676acc2b-e3aa-4509-a9f1-834459ff3418",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:32.885+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:32.885+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "IMAP Home",
"id": "d067bfc2-f66f-4447-b636-53fe6ec95377",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:32.845+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:32.845+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Shared",
"id": "b6557d7e-a032-4da4-85cc-c26fa2c32b8b",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:35.908+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:53.593+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Sites",
"id": "0f972ddd-39e4-489d-a9b6-bc5741738634",
"nodeType": "st:sites",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
{
"entry": {
"createdAt": "2018-07-04T12:57:32.835+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:32.835+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "User Homes",
"id": "f02a301d-db13-4fc2-b9e7-aa0bac9c6b1d",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
}
]
}
}
If you are familiar with Alfresco, then you will recognize the folders that are returned in the response
(/Data Dictionary
, /Guest Home
etc). The response format is standardized and you will see it used for other APIs
that return a list of entities. First is a pagination
object with information about the total number of items
available and if any items were skipped. Then follows an array called entries
with the actual content. Each content
item (in this case a folder or file) is contained in an entry
object.
The new v1 REST APIs are following a “performance first” principle, meaning by default, they only return the data that is the most performant to collect. More data can be requested but the client has to make a conscious decision to request more data and sacrifice a slight performance hit. We will see how to request more data back later in this section.
Let’s have a more detailed look at the URL and explain the different parts:
http://localhost:8080/alfresco
: the ACS server we are talking to/api
: the Alfresco ReST API v1 is accessed/-default-
: the default tenant is accessed (not usually changed unless you are running a multi tenant installation)/public
: this is a supported public endpoint in the ReST API (in contrast to an internal or custom API endpoint)/alfresco
: the Core API is accessed/versions/1
: version 1 of the API is used/nodes
: working with the node entity, which represents things like folders and files/-root-
: specifically working with the top level folder node entity instance/children
: execute the children operation on the top level folder?skipCount=0
: return all nodes from beginning, don’t skip any&maxItems=100
: return max 100 nodes (folders and files)
There’s a lot more stuff you can configure when making the /nodes/{id}/children
call.
You might have noticed in the response that aspects (also known as secondary types) are not returned. To return aspects for each entry you have to use the include
query parameter as follows:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?include=aspectNames&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3170 100 3170 0 0 11321 0 --:--:-- --:--:-- --:--:-- 11321
{
"list": {
"pagination": {
"count": 7,
"hasMoreItems": false,
"totalItems": 7,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2018-07-04T12:57:32.509+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:55.796+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Data Dictionary",
"id": "ec5e2242-e2cf-4fca-977c-f980efa289aa",
"nodeType": "cm:folder",
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
...
Now we get an extra array called aspectNames
for each entry
with all the aspects that have been applied to the node.
However, the properties for these aspects are not returned, such as cm:title
for the cm:titled
aspect. For the
properties to be returned we need to add the properties
value to the include
parameter:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?include=aspectNames,properties&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3947 100 3947 0 0 49337 0 --:--:-- --:--:-- --:--:-- 49337
{
"list": {
"pagination": {
"count": 7,
"hasMoreItems": false,
"totalItems": 7,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2018-07-04T12:57:32.509+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:55.796+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Data Dictionary",
"id": "ec5e2242-e2cf-4fca-977c-f980efa289aa",
"nodeType": "cm:folder",
"properties": {
"cm:title": "Data Dictionary",
"cm:description": "User managed definitions",
"app:icon": "space-icon-default"
},
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
},
...
Now, that’s better. We now got an extra properties
object with all the extra properties contained in the aspects.
The responses we got so far contain quite a lot of data. What if we are developing a mobile Alfresco client, then we
might want to save on the bandwidth and only send back just the data we are going to display. We can achieve that by
using another query parameter called fields. Define exactly the fields
(i.e. content model properties) that you want
in the response:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?include=aspectNames,properties&fields=nodeType,name&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1680 100 1680 0 0 25074 0 --:--:-- --:--:-- --:--:-- 25454
{
"list": {
"pagination": {
"count": 7,
"hasMoreItems": false,
"totalItems": 7,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"name": "Data Dictionary",
"nodeType": "cm:folder",
"properties": {
"cm:title": "Data Dictionary",
"cm:description": "User managed definitions",
"app:icon": "space-icon-default"
}
}
},
...
The include
parameter works independently from the fields
parameter, so to slim it down even further and not include
aspect information you can remove the include
parameter:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?fields=nodeType,name&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 497 100 497 0 0 9940 0 --:--:-- --:--:-- --:--:-- 9940
{
"list": {
"pagination": {
"count": 7,
"hasMoreItems": false,
"totalItems": 7,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"name": "Data Dictionary",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Guest Home",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Imap Attachments",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "IMAP Home",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Shared",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Sites",
"nodeType": "st:sites"
}
},
{
"entry": {
"name": "User Homes",
"nodeType": "cm:folder"
}
}
]
}
}
Another really useful parameter of the /children
operation is the relativePath
parameter. It can be used to navigate
to another folder relative to the /{id}
folder, in this case relative to -root-
. So if I wanted to list the children
of the Data Dictionary folder I wouldn’t have to know the Alfresco Node Identifier (e.g. d8f561cc-e208-4c63-a316-1ea3d3a4e10e)
for the Data Dictionary
folder. I can do a call as follows instead using the relativePath
parameter:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?relativePath=Data%20Dictionary&fields=nodeType,name&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1461 100 1461 0 0 18037 0 --:--:-- --:--:-- --:--:-- 18262
{
"list": {
"pagination": {
"count": 22,
"hasMoreItems": false,
"totalItems": 22,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"name": "Email Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Imap Configs",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Messages",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Models",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Node Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Presentation Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Publishing Root",
"nodeType": "pub:Environment"
}
},
{
"entry": {
"name": "Rendering Actions Space",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Replication Actions Space",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "RSS Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Saved Searches",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Scheduled Actions",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Scripts",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Smart Folder Downloads",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Smart Folder Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Solr Facets Space",
"nodeType": "srft:facets"
}
},
{
"entry": {
"name": "Space Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Transfers",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Web Client Extension",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Web Scripts",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Web Scripts Extensions",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Workflow Definitions",
"nodeType": "cm:folder"
}
}
]
}
}
The relativePath
parameter can be used to navigate down several folders, such as in this example where we list contents
of the /Company Home/Data Dictionary/Email Templates
folder:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?relativePath=Data%20Dictionary/Email%20Templates&fields=nodeType,name&skipCount=0&maxItems=100' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 488 100 488 0 0 6506 0 --:--:-- --:--:-- --:--:-- 6506
{
"list": {
"pagination": {
"count": 6,
"hasMoreItems": false,
"totalItems": 6,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"name": "activities",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Following Email Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "invite",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Invite Email Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Notify Email Templates",
"nodeType": "cm:folder"
}
},
{
"entry": {
"name": "Workflow Notification",
"nodeType": "cm:folder"
}
}
]
}
}
So far, we’ve only used -root-
as the folder id, we can of course also use an explicit node id, for example to retrieve
the children of my /Company Home/Data Dictionary
folder I would use the following URL:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/ec5e2242-e2cf-4fca-977c-f980efa289aa/children
Filter contents of a folder
Listing the contents of a folder in the repository is really useful, here we also cover how to filter the contents we are listing.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/listNodeChildren
See also: Listing contents of a folder
When you know how to list the contents of a folder the next step is usually to filter out content that you are not interested in. Such as filtering out anything that is not of a specific content type.
We use the same URL as when we list the contents of a folder, just with an extra parameter called where
:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/children?where=(<filter expression>)
You can think of the where clause in a similar way to how you would think of it in a database query. Firstly, the isFile
property can be used to just return files that represent content with type cm:content
or a subtype:
nodes/{id}/children?where=(isFile=true)
The same result can be achieved by using the isFolder
property too:
nodes/{id}/children?where=(isFolder=false)
To filter the results by a specific content type nodeType
can be used in the where clause, for example, to retrieve
just the Sites folder use the following URL:
nodes/{id}/children?where=(nodeType=st:sites)
This will result in a single result as shown below:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?where=(nodeType=st:sites)' | jq
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-10-03T08:37:18.732+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-03T08:37:29.544+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Sites",
"id": "c1a73d2a-9a5b-422b-8c95-77e694b248a3",
"nodeType": "st:sites",
"parentId": "695c2c56-3ba0-4539-b301-12bd9bb47712"
}
}
]
}
}
To retrieve all nodes of a specific type and its subtypes use the INCLUDESUBTYPES
moniker, for example:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' "http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?relativePath=My%20Stuff&where=(nodeType='acme:document INCLUDESUBTYPES')" | jq
In this example we are listing content in the /Company Home/My Stuff
folder that has the node type acme:document
or
a subtype of this type.
Finally, the items returned can also be ordered via the orderBy
query parameter. By default, the nodes/{id}/children
endpoint uses orderBy=isFolder DESC,name ASC
as the default sort order, which means folders first alphabetically
followed by files. To mix files and folders and order them alphabetically in reverse order use the following URL:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' "http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?relativePath=My%20Stuff&orderBy=name%20DESC" | jq
Get folder/file metadata
Getting the metadata for a node returns the properties for the node type and applied aspects.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/getNode
Sometimes it is useful to get all metadata (i.e. everything except the content, such as content type, aspects, properties, associations etc) for just one node (e.g. folder, file etc) in the Repository. This can be done with the following HTTP GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}
The {id}
part can be any of the constants -root-
, -my-
, -shared-
or an Alfresco Node Identifier
(e.g. d8f561cc-e208-4c63-a316-1ea3d3a4e10e). The relativePath
parameter can also be used in combination with the id
.
To get the metadata, meaning all properties, for the root folder in the Repository (i.e. /Company Home
) make the following call:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 502 100 502 0 0 38615 0 --:--:-- --:--:-- --:--:-- 38615
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2018-07-04T12:57:32.431+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:37.547+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Company Home",
"id": "444be4a6-5693-4d10-af4b-b55448fe4f97",
"nodeType": "cm:folder",
"properties": {
"cm:title": "Company Home",
"cm:description": "The company root space",
"app:icon": "space-icon-default"
}
}
}
When you request data for a single node the aspects and properties are returned by default, in contrast to when listing children of a node.
We can see the Alfresco Node Identifier in the response above (i.e. id
). We can use it instead of the -root-
constant:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/444be4a6-5693-4d10-af4b-b55448fe4f97' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 502 100 502 0 0 11952 0 --:--:-- --:--:-- --:--:-- 11952
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2018-07-04T12:57:32.431+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "System",
"displayName": "System"
},
"modifiedAt": "2018-07-04T12:57:37.547+0000",
"modifiedByUser": {
"id": "System",
"displayName": "System"
},
"name": "Company Home",
"id": "444be4a6-5693-4d10-af4b-b55448fe4f97",
"nodeType": "cm:folder",
"properties": {
"cm:title": "Company Home",
"cm:description": "The company root space",
"app:icon": "space-icon-default"
}
}
}
Response should be the same.
When fetching metadata for a node via the GET /nodes/{id}
call you can add an extra parameter called include
and use
it to request extra data in the response. One of the values the include parameter can have is association
, which will
return the parent association type for the node. In the following example we request the parent association for the
/Company Home/Data Dictionary
node as follows (note also the use of the relativePath
parameter):
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-?relativePath=Data%20Dictionary&include=association' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 631 0 631 0 0 7170 0 --:--:-- --:--:-- --:--:-- 7170
{
"entry": {
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2020-01-13T14:32:38.482+0000",
"association": {
"isPrimary": true,
"assocType": "cm:contains"
},
"nodeType": "cm:folder",
"parentId": "e4cccfba-bb11-4e8a-965a-e7389cc3bc89",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2020-01-13T14:32:02.506+0000",
"isFolder": true,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Data Dictionary",
"id": "1bd5508f-8e3c-4539-92fd-bbe0d67d30c6",
"properties": {
"cm:title": "Data Dictionary",
"cm:description": "User managed definitions",
"app:icon": "space-icon-default"
}
}
}
At first you might get the idea that the include=association
parameter would give a collection of all child and peer
associations for the node. But it will actually just return the primary parent type as in the above example
(and note that the association
value is actually not written in plural):
...
"association": {
"isPrimary": true,
"assocType": "cm:contains"
},
...
If you wanted to get all the peer associations for a node have a look at this page instead,
it explains how you can use GET /nodes/{id}/sources
and GET /nodes/{id}/targets
calls for that. And you can of
courser get the parent-child associations for a node with the GET /nodes/{id}/children
call.
There are more data that you can include in the response, such as folder path to the node and permissions set on the node.
In the following example we request this extra data by setting include=association,path,permissions
:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-?relativePath=Data%20Dictionary/Email%20Templates&include=association,path,permissions' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1212 0 1212 0 0 39096 0 --:--:-- --:--:-- --:--:-- 39096
{
"entry": {
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2020-01-13T14:32:11.005+0000",
"association": {
"isPrimary": true,
"assocType": "cm:contains"
},
"nodeType": "cm:folder",
"parentId": "1bd5508f-8e3c-4539-92fd-bbe0d67d30c6",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
],
"createdAt": "2020-01-13T14:32:02.642+0000",
"path": {
"name": "/Company Home/Data Dictionary",
"isComplete": true,
"elements": [
{
"id": "e4cccfba-bb11-4e8a-965a-e7389cc3bc89",
"name": "Company Home",
"nodeType": "cm:folder",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
]
},
{
"id": "1bd5508f-8e3c-4539-92fd-bbe0d67d30c6",
"name": "Data Dictionary",
"nodeType": "cm:folder",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
]
}
]
},
"isFolder": true,
"permissions": {
"inherited": [
{
"authorityId": "GROUP_EVERYONE",
"name": "Consumer",
"accessStatus": "ALLOWED"
}
],
"settable": [
"Contributor",
"Collaborator",
"Coordinator",
"Editor",
"Consumer"
],
"isInheritanceEnabled": true
},
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Email Templates",
"id": "25ea1b4b-23c0-412e-ae71-e6a9ec56d072",
"properties": {
"cm:title": "Email Templates",
"cm:description": "Email templates",
"app:icon": "space-icon-default"
}
}
}
Note the new path
and permission
properties in the response.
Create a folder
Creating a folder means creating a node with metadata.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/createNode
See also:
- How to update metadata - includes information about multi-value properties
- How to add aspects
- How to manage associations (contains examples of creating folder)
When we are familiar with how to list the contents of a folder, and fetch metadata for a folder or file, then it makes sense to start looking at creating nodes. Let’s start by looking at how to create a folder. The following HTTP POST call is used:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/children
So we are basically making a POST call to the children collection for a folder when we are creating a node, such as a
folder. The {id}
part represents the folder under which we want to create a sub-folder. The id
can be either one of
the constants -root-
, -shared-
, -my-
or an Alfresco Node Identifier (e.g. 444be4a6-5693-4d10-af4b-b55448fe4f97).
The POST data defines the folder metadata and looks like in this example:
{
"name": "My Folder",
"nodeType": "cm:folder",
"properties": {
"cm:title": "My Folder",
"cm:description": "My new folder"
}
}
Here is how to create a folder with the name My Folder
directly under the /Company Home
(i.e. -root-
) folder:
$ curl -H "Content-Type: application/json" -d '{"name":"My Folder","nodeType":"cm:folder", "properties": { "cm:title":"My Folder", "cm:description":"My new folder"}}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 622 100 503 100 119 10702 2531 --:--:-- --:--:-- --:--:-- 13234
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable"
],
"createdAt": "2019-09-02T12:24:52.887+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-02T12:24:52.887+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "My Folder",
"id": "1e8d8df6-7d33-4966-83db-0333a7f6277a",
"nodeType": "cm:folder",
"properties": {
"cm:title": "My Folder",
"cm:description": "My new folder"
},
"parentId": "444be4a6-5693-4d10-af4b-b55448fe4f97"
}
}
When you create a node a JSON object is returned with the data that was created by the system, such as the properties of the cm:auditable
aspect (i.e. cm:created
, cm:creator
etc). The generated Alfresco Node Identifier (i.e. id
) is also returned.
You may have noticed that slightly more information about the node (aspectNames
and properties
) is returned by default even through we are still using a “performance first” principle. In the same way the include
parameter is used when listing nodes, it can also be used when creating nodes to list extra information or limit what is listed.
Upload a file
Uploading a file to the Repository means creating a node with metadata and content.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/createNode
See also:
- How to update metadata - includes information about multi-value properties
- How to add aspects
- How to manage associations (contains examples of uploading files)
Creating a file is slightly different from creating a folder as a file also has content. The following HTTP POST call is used (same as when creating other types of nodes, such as folders):
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/children
So we are basically making a POST call to the children collection for a folder when we are creating a file. The {id}
part represents the folder under which we want to create the file. The id
can be either one of the constants -root-
,
-shared-
, -my-
or an Alfresco Node Identifier (e.g. d8f561cc-e208-4c63-a316-1ea3d3a4e10e).
As well as accepting JSON when creating a node with metadata, the same endpoint (i.e. nodes/{id}/children
) also accepts
multipart/form-data
, allowing us to upload content from a standard HTML form or from the command line using curl.
When we make the POST with curl we have to do it as form data submission, with each form field specified with -F
. The
filedata
field will point to the contents of the file we are uploading. The name
field specifies the name we want to
give the file when it’s stored in the Repository (i.e. cm:name
). The nodeType
field is used to set the content type
that the file should have. We can use the relativePath
field to store the file in a different location relative to the
{id}
. Any other field that we specify will be treated as a property that should be set on the node (e.g. cm:title
).
Here is how to create a file under a folder called My Folder
, which is located under the root folder (i.e. -root-
).
If the folder does not exist, then it will be created:
$ curl -X POST -F filedata=@test.txt -F "name=somefile.txt" -F "nodeType=cm:content" -F "cm:title=My text" -F "cm:description=My text document description" -F "relativePath=My Folder" -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1483 100 702 100 781 4680 5206 --:--:-- --:--:-- --:--:-- 9886
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-05T08:58:24.463+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
},
"parentId": "5a858591-752f-49d0-b686-e2e1a830ea8d",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author"
],
"createdAt": "2019-09-05T08:58:24.463+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "0305fc9c-fc1d-405a-abf0-af482a9239ec",
"properties": {
"cm:title": "My text",
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:description": "My text document description"
}
}
}
The call returns an entry
object with all the information about the new file node. Note that versioning is turned on by default.
When uploading content it’s quite common for a file with the same name to exist, this will generate an error by default,
to avoid the error the autoRename
form field can be set to true
. If a filename clash is detected a suffix will be
added to the filename, for example my-file.txt
will become my-file-1.txt
.
Let’s try and upload the somefile.txt
again as above but with the autoRename
parameter set to true
:
$ curl -X POST -F filedata=@test.txt -F "name=somefile.txt" -F "nodeType=cm:content" -F "cm:title=My text" -F "cm:description=My text document description" -F "relativePath=My Folder" -F "autoRename=true" -H 'Authorization: Basic VElDS0VUX2I1YmVjODNkZTQ2ZDI5NDAzMTMzZTk2N2EwYjNjYmE5NjExYmYzOWY=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1615 0 704 100 911 3555 4601 --:--:-- --:--:-- --:--:-- 8197
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-07T09:36:10.410+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 61,
"encoding": "ISO-8859-1"
},
"parentId": "e41886ee-f2f5-49aa-b081-ce13c3536032",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author"
],
"createdAt": "2019-10-07T09:36:10.410+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile-1.txt",
"id": "7dc41a62-3cc8-425b-bbfd-bbf383c6168f",
"properties": {
"cm:title": "My text",
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:description": "My text document description"
}
}
}
We can see that the file was created with the name somefile-1.txt
as expected.
Another feature of the repository we can control when uploading content is the generation of a rendition. To have the
Share thumbnail rendition generated, provide a renditions
form field with a value of doclib
as shown here:
$ curl -X POST -F filedata=@test.txt -F "name=somefile.txt" -F "nodeType=cm:content" -F "cm:title=My text" -F "cm:description=My text document description" -F "relativePath=My Folder" -F "autoRename=true" -F "renditions=doclib" -H 'Authorization: Basic VElDS0VUX2I1YmVjODNkZTQ2ZDI5NDAzMTMzZTk2N2EwYjNjYmE5NjExYmYzOWY=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
Currently only one rendition can be requested, there are plans to allow multiple in the future hence the plural form field name.
Upload a file with custom type
Uploading a file with a custom type to the Repository means creating a node with a type other than cm:content
.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/createNode
See also:
- How to update metadata - includes information about multi-value properties
- How to add aspects
- How to manage associations (contains examples of uploading files)
In the last section we saw how to upload a file and set an out-of-the-box content type (i.e. cm:content
). Another common
scenario is to upload a file and set a custom content type. To demonstrate this we need to first apply a custom content
model to the Repository.
The following steps show how to create a custom content model that can be deployed to ACS
-
Describe the custom content model with XML
Let’s use the following content model, which defines the custom type
acme:document
:<?xml version="1.0" encoding="UTF-8"?> <model name="acme:contentModel" xmlns="http://www.alfresco.org/model/dictionary/1.0"> <description>Sample Document Model</description> <author>My Name</author> <version>1.0</version> <imports> <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/> <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/> <import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/> </imports> <namespaces> <namespace uri="http://www.acme.org/model/content/1.0" prefix="acme"/> </namespaces> <constraints> <constraint name="acme:securityClassificationOptions" type="LIST"> <parameter name="allowedValues"> <list> <value></value> <value>Public</value> <value>Client Confidential</value> <value>Company Confidential</value> <value>Strictly Confidential</value> </list> </parameter> </constraint> </constraints> <types> <type name="acme:document"> <title>Sample Document Type</title> <parent>cm:content</parent> <properties> <property name="acme:documentId"> <title>Document Identification Number</title> <type>d:text</type> </property> </properties> <mandatory-aspects> <aspect>acme:securityClassified</aspect> </mandatory-aspects> </type> </types> <aspects> <aspect name="acme:securityClassified"> <title>ACME Security Classified</title> <description>Content has been security classified</description> <properties> <property name="acme:securityClassification"> <type>d:text</type> <index enabled="true"> <atomic>true</atomic> <stored>false</stored> <tokenised>false</tokenised> </index> <constraints> <constraint ref="acme:securityClassificationOptions"/> </constraints> </property> </properties> </aspect> </aspects> </model>
This model also has a custom aspect
acme:securityClassified
, so we can see how aspects can be applied at the same time as we set a custom content type. Store the XML in a file called, for example,acme-content-model.xml
. -
Create a file that bootstraps the content model
For the custom content model to be applied to the Repository we need to define a bootstrap file that points to the
acme-content-model.xml
file. Create a file calledacme-bootstrap-context.xml
with the following XML:<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'> <beans> <bean id="acme.extension.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap"> <property name="models"> <list> <value>alfresco/extension/acme-content-model.xml</value> </list> </property> </bean> </beans>
-
Create a directory somewhere for Repository extension files and copy the files there
Now, copy both the
acme-content-model.xml
file and theacme-bootstrap-context.xml
file into this new directory. This directory could also contain theapi-explorer.war
if you have applied it to your ACS installation.There are now two different ways of deploying this content model depending on if you are using an ACS Trial or an Alfresco SDK project.
If you are using a trial version of ACS then you follow the first approach described below. If you are using the Alfresco SDK you would want to follow the second approach. The main difference between the two is that the first one will lead to loss of the data that you are working with as the ACS trial Docker Compose file does not use volumes (i.e. it does not store data externally but instead inside the container).
Installing the custom content model into an ACS Trial environment
IMPORTANT! you will lose your data/content when you do this
-
Create a
Dockerfile
for the new custom Repository Docker ImageAs a developer you are most likely running ACS 6.x via a Docker Compose file, either via trial or SDK. The
Dockerfile
will be based on the Repository Image that is used in thedocker-compose.xml
file. Have a look in it and you should see that it starts with defining the Alfresco Repository Docker image:version: "2" services: alfresco: image: alfresco/alfresco-content-repository:6.1.0 ...
With this information we know what Docker Image to base our custom Repo Docker Image on. Create a
Dockerfile
in the same directory as the content model XML files and have it look like this:FROM alfresco/alfresco-content-repository:6.1.0 ARG TOMCAT_DIR=/usr/local/tomcat # Make sure we got the ReST API Explorer available COPY api-explorer.war $TOMCAT_DIR/webapps/ # Copy in the custom content model for upload file example COPY acme-content-model.xml acme-bootstrap-context.xml $TOMCAT_DIR/shared/classes/alfresco/extension/
What this
Dockerfile
will do is build a custom Repository Docker image that is based on the out-of-the-box Alfresco Repository Docker image that you are using. It will then copy in theapi-explorer.war
into thetomcat/webapps
directory followed by a copy of the custom content model files into an extension directory where they can be picked up and bootstrapped. -
Build the custom Alfresco Repository Docker image
When we got the Dockerfile completed we just need to build the custom Docker image as follows, standing in the directory with all the files:
repo mbergljung$ docker build -t alf-repo-custom:1.0 . Sending build context to Docker daemon 954.4kB Step 1/4 : FROM alfresco/alfresco-content-repository:6.1.0 ---> 5439a493ee0a Step 2/4 : ARG TOMCAT_DIR=/usr/local/tomcat ---> Using cache ---> cf2a1261adf4 Step 3/4 : COPY api-explorer.war $TOMCAT_DIR/webapps/ ---> Using cache ---> 52f10e1f00d6 Step 4/4 : COPY acme-content-model.xml acme-bootstrap-context.xml $TOMCAT_DIR/shared/classes/alfresco/extension/ ---> 1766782c545a Successfully built 1766782c545a Successfully tagged alf-repo-custom:1.0
Check that you got the custom Docker image:
repo mbergljung$ docker image ls |grep alf- alf-repo-custom 1.0 1766782c545a About a minute ago 1.16GB
-
Update the
docker-compose.xml
file to use the new custom imageOpen up the
docker-compose.xml
file and change it so the Repository service is based on the custom Docker Image we just created. It should now look something like this:version: "2" services: alfresco: image: alf-repo-custom:1.0 ...
-
Restart ACS
We have made changes only to the Repository container, also known as the alfresco Docker Compose service, but we need to remove and restart all containers so data is in sync (basically we are starting over with an empty repository). After we have created our own Docker Image for the Alfresco Repository container and configured Docker Compose with it we can restart as follows by doing Ctrl-C out of the log, this will stop all containers, we then remove them, followed by starting it up again:
^CGracefully stopping... (press Ctrl+C again to force) Stopping acs61_alfresco-pdf-renderer_1 ... done ... acs61 mbergljung$ docker-compose rm Going to remove acs61_alfresco-pdf-renderer_1, acs61_transform-router_1, acs61_libreoffice_1, acs61_tika_1, acs61_imagemagick_1, acs61_proxy_1, acs61_share_1, acs61_postgres_1, acs61_digital-workspace_1, acs61_alfresco_1, acs61_activemq_1, acs61_solr6_1, acs61_shared-file-store_1 Are you sure? [yN] y Removing acs61_alfresco-pdf-renderer_1 ... done ... acs61 mbergljung$ docker-compose up Creating acs61_alfresco-pdf-renderer_1 ... done ...
Installing the custom content model into an Alfresco SDK AIO project
-
Verify what content model that is currently defined in the SDK project
When you generate an Alfresco SDK project, such as an All-In-One (AIO) project, it comes with a predefined content model that already includes the
acme:document
type and theacme:securityClassified
aspect. Look in theaio/acs-aio-platform/src/main/resources/alfresco/module/acs-aio-platform/model/content-model.xml
file and check what is currently defined. If you see the type and the aspect, then you don’t have to copy the files into the SDK project. -
(OPTIONAL) Copy the content model files into the SDK project
Copy both the
acme-content-model.xml
file and theacme-bootstrap-context.xml
file into theaio/aio-platform-docker/src/main/docker
AIO SDK directory. -
(OPTIONAL) Open up the platform/repository
Dockerfile
and add the command to copy the content model files into analfresco/extension
directoryThe platform (repository) Docker file is located in the
aio/aio-platform-docker/src/main/docker
AIO SDK directory. Add the following COPY command at the end of this file:... # Copy in the custom content model for upload file example COPY acme-content-model.xml acme-bootstrap-context.xml $TOMCAT_DIR/shared/classes/alfresco/extension/
What this Dockerfile will do is build a custom Repository Docker image that is based on the out-of-the-box Alfresco Repository Docker image that you are using. After it has copied in all the extensions, config files, license etc it will finish by copying in the
acme-content-model.xml
andacme-bootstrap-context.xml
files into thetomcat/shared/classes/alfresco/extension
directory where it will be picked up and deployed. -
(OPTIONAL) Restart the platform/repository container
We have changed only the platform/repository, so it is enough to just restart this container:
acs61-aio mbergljung$ ./run.sh reload_acs Killing docker_acs61-aio-acs_1 ... done Going to remove docker_acs61-aio-acs_1 Removing docker_acs61-aio-acs_1 ... done ...
Note. this does not remove any content or metadata.
Test uploading file and setting custom type
We now got a custom content model applied with a custom content type called acme:document
. We can use it when uploading
a file as follows:
$ curl -X POST -F filedata=@test.txt -F "name=somefile.txt" -F "nodeType=acme:document" -F "acme:documentId=DOC001" -F "aspectNames=acme:securityClassified" -F "acme:securityClassification=Public" -F "cm:title=My text" -F "cm:description=My custom text document description" -F "relativePath=My Custom Folder" -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1957 100 804 100 1153 761 1091 0:00:01 0:00:01 --:--:-- 1853
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-09T07:38:55.060+0000",
"nodeType": "acme:document",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
},
"parentId": "69470c63-ea8f-4a93-a408-673d5668e369",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:auditable",
"acme:securityClassified",
"cm:author"
],
"createdAt": "2019-09-09T07:38:55.060+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "d8f561cc-e208-4c63-a316-1ea3d3a4e10e",
"properties": {
"cm:title": "My text",
"acme:securityClassification": "Public",
"cm:versionType": "MAJOR",
"acme:documentId": "DOC001",
"cm:versionLabel": "1.0",
"cm:description": "My custom text document description"
}
}
}
We can see that the custom content type (i.e. acme:document
) has been set correctly, including the property it
contains (i.e. acme:documentId
). At the same time we also show how to apply new aspects with the aspectNames
field
(note that some aspects are set automatically when you upload a file, such as cm:author
, cm:auditable
etc).
So this call is quite powerful.
Upload a new version of file
Uploading a new version of a file means replacing the content and creating a new entry in the version history.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/updateNodeContent
See also:
When we have files in the Repository it is common to want to update the content for them. The following HTTP PUT call is used to upload a new version of a file:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/content?majorVersion=[true|false]&comment=<comment about what changed>
We need to specify the Node Identifier (i.e. {id}
) for the file node that should have its content updated. You can
choose to do a major version update or a minor version update using the majorVersion
parameter. A comment about the
content update can be specified with the comment
parameter.
Here is how to update a text file with Node Identifier d8f561cc-e208-4c63-a316-1ea3d3a4e10e and make a major version change:
$ curl -X PUT -H 'Content-Type: text/plain' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d 'This text file has had a major change...' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/content?majorVersion=false&name=somefile.txt' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 991 100 951 100 40 1571 66 --:--:-- --:--:-- --:--:-- 1635
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-09T09:14:23.914+0000",
"nodeType": "acme:document",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 40,
"encoding": "ISO-8859-1"
},
"parentId": "69470c63-ea8f-4a93-a408-673d5668e369",
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"acme:securityClassified",
"cm:failedThumbnailSource",
"cm:author",
"cm:thumbnailModification"
],
"createdAt": "2019-09-09T07:38:55.060+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "d8f561cc-e208-4c63-a316-1ea3d3a4e10e",
"properties": {
"cm:title": "My text",
"cm:versionType": "MINOR",
"acme:documentId": "DOC001",
"cm:versionLabel": "1.1",
"acme:securityClassification": "Public",
"cm:lastThumbnailModification": [
"pdf:1568015091834",
"doclib:1568015093159"
],
"cm:description": "My custom text document description"
}
}
}
The call returns an entry
object with all the information about the updated file node. Note that the cm:versionLabel
has changed to 1.1
as we choose to do a minor version update. If we would have done a major version update, then it
would have been set to 2.0. When versioning is turned on for a file, such as in this case, each previous version of the
file is saved on disk.
Get file version history
When a file has versioning turned on you can get its version history.
API Explorer URL: http://localhost:8080/api-explorer/#!/versions/listVersionHistory
See also: Upload a new version of a file
When we have a file with versioning turned on (i.e. the cm:versionable
aspect applied) we can retrieve its version
history. This will give us information about all the previous versions of the file that are available for download.
The following HTTP GET call is used to fetch the version history:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/versions
We need to specify the Node Identifier (i.e. {id}
) for the versioned file node.
Here is how to fetch the version history for a text file with Node Identifier 90d0dd09-93d2-448c-9c23-24de24c3f6ff:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUX2M3YzdkOWFjODA2ZDRiNTNhNIxMjA3NTU1MDM4NDFmMWUzMmMyNjM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/90d0dd09-93d2-448c-9c23-24de24c3f6ff/versions' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1461 0 1461 0 0 41742 0 --:--:-- --:--:-- --:--:-- 41742
{
"list": {
"pagination": {
"count": 4,
"hasMoreItems": false,
"totalItems": 4,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"isFolder": false,
"isFile": true,
"modifiedAt": "2019-10-07T13:49:54.736+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "1.3",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 473,
"encoding": "UTF-8"
}
}
},
{
"entry": {
"isFolder": false,
"isFile": true,
"modifiedAt": "2019-10-07T13:49:17.597+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "1.2",
"nodeType": "cm:content",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 473,
"encoding": "UTF-8"
}
}
},
{
"entry": {
"isFolder": false,
"isFile": true,
"modifiedAt": "2019-10-07T13:48:42.333+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "docker-compose.yml",
"versionComment": "Some minor changes to the text",
"id": "1.1",
"nodeType": "cm:content",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 473,
"encoding": "UTF-8"
}
}
},
{
"entry": {
"isFolder": false,
"isFile": true,
"modifiedAt": "2019-10-07T13:48:42.333+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "docker-compose.yml",
"id": "1.0",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 19,
"encoding": "UTF-8"
}
}
}
]
}
}
The call returns a pagination
object with information about the number of versions available for this file, in this
case 4, which are described in entry
objects. To retrieve the content for a version other than the latest, which is
returned when getting content for a file, use the following GET call:
/nodes/{nodeId}/versions/{versionId}/content
So, to get the content for the first version of this file use the versionId
1.0 as follows:
$ curl -X GET -H 'Accept: text/plain' -H 'Authorization: Basic VElDS0VUX2M3YzdkOWFjONTU1MDM4NDFmMWUzMmMyNjM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/90d0dd09-93d2-448c-9c23-24de24c3f6ff/versions/1.0/content'
This is a text file
The versionId
is the same as the version label.
To revert to a previous version of the file we have to POST the following:
{
"majorVersion": true,
"comment": "Reverted to original"
}
To the following URL:
/nodes/{nodeId}/versions/{versionId}/revert
We are able to specify whether the reverted version will create a new minor or major version and again provide a comment describing the reason for the additional version.
Download a file
Downloading the file means getting the file content from the Repository, which has it stored on disk.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/getNodeContent
See also: Downloading multiple files
When you know how to upload content to the Repository it is natural to want to download content from the Repository. The following HTTP GET call is used to download a file:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/content
Here we are getting the content for a file with the Node Identifier specified as the {id}
URL part.
The following example gets content for a text file with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier:
$ curl -X GET -H 'Accept: text/plain' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/content
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...
You can also just make this call (i.e. http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/c4980e44-92ac-41a5-91dc-2b2183c61de8/content
)
in a Web Browser and the file will be downloaded. If you want to preview the file in the Web Browser you would need to
add an extra query parameter called attachment
and set it to false
(i.e.
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/c4980e44-92ac-41a5-91dc-2b2183c61de8/content?attachment=false
).
Download multiple files
It’s possible to download multiple files as a ZIP.
API Explorer URL: http://localhost:8080/api-explorer/#!/downloads/createDownload
See also: Downloading a single file
Sometimes it is useful to be able to download multiple files in one go. You can create such a download with the following HTTP POST call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/downloads
You POST the following type of body with the Node Identifiers for the files that should be downloaded:
{
"nodeIds":
[
"node identifier for file 1",
"node identifier for file 2",
"node identifier for file 3",
and so on...
]
}
The response to this call will contain a ZIP Download Node Identifier that can be used in subsequent calls to check the status of the download and ultimately download the archive. This call will start an asynchronous process on the server side that will put together a ZIP package with the files. You can check the status of this process with the following HTTP GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/downloads/{id}
Here we are getting the status for a ZIP download with the Node Identifier specified as the {id}
URL part.
Let’s see how this works with an example. I got two text files with Node Identifiers 7279b5c5-da55-4e98-8b12-72d33b90c810 and 1cf35d69-f85f-4446-94cd-f31ccf16c2e3 that I want to download. So the POST data will look like this:
{
"nodeIds":
[
"7279b5c5-da55-4e98-8b12-72d33b90c810",
"1cf35d69-f85f-4446-94cd-f31ccf16c2e3"
]
}
The call will then look like this:
$ curl -X POST -H "Content-Type: application/json" -H 'Accept: application/json' -d '{"nodeIds":["7279b5c5-da55-4e98-8b12-72d33b90c810","1cf35d69-f85f-4446-94cd-f31ccf16c2e3"]}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/downloads | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 225 0 134 100 91 822 558 --:--:-- --:--:-- --:--:-- 1380
{
"entry": {
"filesAdded": 0,
"bytesAdded": 0,
"totalBytes": 0,
"id": "3ea71d75-e2fa-4f23-9239-438c9b048574",
"totalFiles": 0,
"status": "PENDING"
}
}
The response contains the status of the download and its id
that we can use later on to check the status and download
the ZIP archive.
We can now make a GET call to the /downloads/{id} URL and check the status until we see status
set to DONE
:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/downloads/3ea71d75-e2fa-4f23-9239-438c9b048574 | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 133 0 133 0 0 6650 0 --:--:-- --:--:-- --:--:-- 6650
{
"entry": {
"filesAdded": 2,
"bytesAdded": 56,
"totalBytes": 56,
"id": "3ea71d75-e2fa-4f23-9239-438c9b048574",
"totalFiles": 2,
"status": "DONE"
}
}
The ZIP file with Node Identifier 3ea71d75-e2fa-4f23-9239-438c9b048574 is now ready to be downloaded. It is stored in
the hidden /sys:system/sys:downloads
folder.
To download the ZIP file follow instructions for downloading a single file. In this case the call to download the ZIP would look like this:
$ curl -X GET -H 'Accept: application/octet-stream' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/3ea71d75-e2fa-4f23-9239-438c9b048574/content
Binary output...
It’s better to try the download from a browser.
Note: The download ZIP file node can be deleted using the delete a file endpoint if needed.
By default, if the download node is not deleted it will be picked up by a cleaner job which removes download nodes older than a configurable amount of time (default is 1 hour).
Get Direct Download URL for a file
Generate a direct access/download content URL for the given file. This will enable downloading the file directly from Cloud storage such as AWS S3.
API Explorer URL: http://localhost:8080/api-explorer/#/nodes/requestNodeDirectAccessUrl
See also: AWS S3 Direct Access URLs
The following HTTP POST call is used to download a file:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{nodeId}/request-direct-access-url
It is up to the actual ContentStore
implementation if it can fulfil this request or not.
The POST body contains an attachment
flag, which controls the download method of the generated URL. It defaults to true
,
meaning the value for the Content-Disposition
response header will be attachment
. true
enables a web browser to
download the file as an attachment. false
means a web browser may preview the file in a new tab or window.
You can only set this parameter to false
if the content type of the file is in the supported list; for example,
certain image files and PDF files. If the content type is not supported for preview, then a value of false
is ignored,
and the attachment will be returned in the response.
{
"attachment": true
}
The following example gets a direct access URL for a text file with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "attachment": true}' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/request-direct-access-url' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
{
"entry": {
"contentUrl": "https://<bucket_name>.s3.<region_name>.amazonaws.com/<binary_name>.bin?response-content-disposition=attachment%3B%20filename%20%3D%22graph.JPG%22&response-content-type=image%2Fjpeg&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEMv%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMiJGMEQCIDmc%2Fb1e55l4sQjKGG3%2Fr1CU0gtzOOqFnr0Q%2BuXoNa%2BXAiB5oSPGJI1%2FZORobOtV%2BUmiim6GMQJxoKT9I%2Fn6t9ANvir6AwgUEAMaDDE3NTEyNTQyOTQ0MiIMA1qC5mzeuQyHnfd%2BKtcDgAHmPq1MEq5lrb2ggn7Ev%2FSJ%2FQgMVB33Y7NyfsD4BTB3Cn7e1uH17uIH8SkHX6tA9cjBOKx6Sym3gzzP2kTdKSPimQ1UOXMw4uhtaI0f%2FkqnI%2BhMh6GZXT6lOfqDE%2Fkz9nM3QuBxaNI2b8Nb71lP0KPmq7bzBagJOIccf2%2BK3VW3en5gS%2FVAoU2Wx8j1HEQJuk%2FS1whspl970hPFXKIFGIbedO5H8P66wOYdb9LKiHVxvNK7cAJfrVT6jnmqf1L6GyRJa01xgOqgUw1LvsqGsf8kkw%2FkWwJz25StcmJLtpLcWsmZ0x8aHmDNi8SHixteB5XXKJ9Bv8Ex0iIMH3%2Bs8uWmBFssu9il6u8GyV%2FlaIhKYcZLLpIFSTtVudWe60UpQhFPqyHZ6gqqi4e%2BZZfGqqhUNbZucqMvc31V76NbvwdHxI%2F0H0I8fVqCtIatO655qtq6sy%2B29qYymE7RLI9Vnrotkz%2FJafHt4LDIOjX3aDcHS0%2FTxr4QmyJbh%2B%2F0JKsSlqyoosUgzi0mqzw0B8zsTlrkfR9dPkQTNntxZoARaddEIA4Q8QRryQLFe8FITeHSFhUpdPXei3ZEmguSUpkqUQroUdQm8W3C2aoV%2F0A%2BS80IaffqNUY6MPawjpAGOqYBSMI0t5Xt7oW8QqGQrDSMllhX18T0UoxNEvYBii6vFzjuKKasQV5WaGtOMhcg8B5Ee7AxXTCl06FSPhmrQ3f%2FtFTqYtbd8FR8QTK0ZJekBMoM5thzFJ4EztnCYrkAnDo1oDUDOuBQxVho8w5llTEaKLo1SgomysnvpRFshJdBl%2BKXuFVM6Q2tmqSCY%2Bmm%2BVVte%2Bt8Yc4Ulg5eZpkkt3g2HOBaI0cnOw%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220209T115428Z&X-Amz-SignedHeaders=host&X-Amz-Expires=30&X-Amz-Credential=ASIASRRSJ7TBNPZVGWOY%2F20220209%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Signature=6b240b52024eca8a07e47dfad6970f84a75de049a1ae7af5855ed8c655f76cda",
"attachment": true,
"expiryTime": "2022-02-09T11:54:58.700+0000"
}
}
Sample response for Azure Connector:
{
"entry": {
"contentUrl": "https://<storage_account_name>.blob.core.windows.net/<container_name>/<blob_name>?sv=2020-10-02&spr=https&se=2022-02-09T04%3A09%3A40Z&sr=b&sp=r&sig=LkznZiG6u2BUDprdKyk0Hm9XkURG%2BZZp0qy0Ls3kgVY%3D&rscd=attachment%3B%20filename%20%3D%22graph.JPG%22&rsct=image%2Fjpeg",
"attachment": true,
"expiryTime": "2022-02-09T04:09:40.638+0000"
}
}
List file renditions
A file can have a number of renditions generated for it. This is how you get a list these renditions.
API Explorer URL: http://localhost:8080/api-explorer/#!/renditions/listRenditions
A file can have a number of renditions for its content, such as a thumbnail or a preview. A rendition is basically a different representation of the file content. To see what renditions that are available for a file use the following HTTP GET call, it returns all renditions wether they have been created or not:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/renditions
Here we are getting the renditions for the file with Node Identifier specified as the {id}
URL part.
The following example gets renditions for a text file with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/renditions | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 867 100 867 0 0 41285 0 --:--:-- --:--:-- --:--:-- 41285
{
"list": {
"pagination": {
"count": 6,
"hasMoreItems": false,
"totalItems": 6,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"id": "avatar",
"content": {
"mimeType": "image/png",
"mimeTypeName": "PNG Image"
},
"status": "NOT_CREATED"
}
},
{
"entry": {
"id": "avatar32",
"content": {
"mimeType": "image/png",
"mimeTypeName": "PNG Image"
},
"status": "NOT_CREATED"
}
},
{
"entry": {
"id": "doclib",
"content": {
"mimeType": "image/png",
"mimeTypeName": "PNG Image",
"sizeInBytes": 432,
"encoding": "UTF-8"
},
"status": "CREATED"
}
},
{
"entry": {
"id": "imgpreview",
"content": {
"mimeType": "image/jpeg",
"mimeTypeName": "JPEG Image"
},
"status": "NOT_CREATED"
}
},
{
"entry": {
"id": "medium",
"content": {
"mimeType": "image/jpeg",
"mimeTypeName": "JPEG Image"
},
"status": "NOT_CREATED"
}
},
{
"entry": {
"id": "pdf",
"content": {
"mimeType": "application/pdf",
"mimeTypeName": "Adobe PDF Document",
"sizeInBytes": 8238,
"encoding": "UTF-8"
},
"status": "CREATED"
}
}
]
}
}
In this case we got back information about six renditions for this text file, each contained in an entry
object.
Note that only two of those have been created and can be downloaded (i.e. they have status
set to CREATED
).
Get file rendition content
Get the rendition file content, if it has been generated.
API Explorer URL: http://localhost:8080/api-explorer/#!/renditions/getRenditionContent
If you know that a file has a rendition you can retrieve the content for it with the following HTTP GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/renditions/{renditionId}/content
Here we are getting the rendition content for the rendition specified with {renditionId}
for the file with
Node Identifier specified as the {id}
URL part.
The following example gets the doclib
rendition (i.e. the thumbnail) content for a text file with the
d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier and stores it in a local file called somerendition.png
:
$ curl -X GET --output somerendition.png -H 'Accept: image/png' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/renditions/doclib/content'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 432 100 432 0 0 19636 0 --:--:-- --:--:-- --:--:-- 19636
Get Direct Download URL for a file rendition
Generate a direct access/download content URL for the given file rendition. This will enable downloading the file rendition directly from Cloud storage such as AWS S3.
API Explorer URL: http://localhost:8080/api-explorer/#/renditions/requestRenditionDirectAccessUrl
The following HTTP POST call is used:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{nodeId}/renditions/{renditionId}/request-direct-access-url
For info on how to execute this request see Get Direct Download URL for a file and use above URL.
Update metadata for a folder or file
Update the properties, also referred to as metadata, for a folder or file.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/updateNode
See also:
Note: It is not possible to include aspects or properties that are part of the
systemModel
(i.e. namespacesys:
) in API calls.
Quite often you want to update some metadata for a folder or file. It can for example be as part of a business process that is used to process content, and at different places in the flow, folders and files should be updated to reflect the processing state. The ReST API implements partial update via PUT. Although technically this is not RESTful it was decided to bend the rules here to keep things as simple as possible for clients, meaning the client only needs to send the data that is changing, with one exception related to aspects.
Metadata is all the information about the folder or file node except the actual content, if it’s a file node. Folder nodes don’t have any content. This is properties, aspects, associations, content type etc.
To update metadata for a node use the following HTTP PUT call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}
The Node Identifier for the folder or file node to be updated is specified with the {id}
parameter. Then a body is
created with all the data that makes up the update.
The following call will update the metadata for a text file identified with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier. The data that is passed in the PUT call looks as follows:
{
"name":"newfilename.txt",
"properties": {
"cm:title":"UPDATED My text file",
"cm:description":"UPDATED My text file description"
}
}
And here is the call:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{"name":"newfilename.txt", "properties": { "cm:title":"UPDATED My text file", "cm:description":"UPDATED My text file description"}}' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e' | jq
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-09T09:58:51.492+0000",
"nodeType": "acme:document",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 40,
"encoding": "ISO-8859-1"
},
"parentId": "69470c63-ea8f-4a93-a408-673d5668e369",
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"acme:securityClassified",
"cm:author",
"cm:thumbnailModification"
],
"createdAt": "2019-09-09T07:38:55.060+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "newfilename.txt",
"id": "d8f561cc-e208-4c63-a316-1ea3d3a4e10e",
"properties": {
"cm:title": "UPDATED My text file",
"cm:versionType": "MINOR",
"acme:documentId": "DOC001",
"cm:versionLabel": "1.1",
"acme:securityClassification": "Public",
"cm:lastThumbnailModification": [
"pdf:1568020467536",
"doclib:1568020468157"
],
"cm:description": "UPDATED My text file description"
}
}
}
In the returned entry
object we can see that the name
of the file has been updated successfully plus the cm:titled
aspect properties.
This PUT call can also be used to rename the node by just providing a cm:name
property in the properties as shown below:
{
"properties":
{
"cm:name":"renamed-name.txt"
}
}
Alternatively, the top level name
property can also be used as in our example above:
{
"name":"renamed-file.txt"
}
Similarly, the owner of the node can be updated, just provide the cm:owner
property as follows:
{
"properties":
{
"cm:owner":"mbergljung"
}
}
There is one exception to the partial update rule and that is for managing aspects. To change the aspects applied to a node the whole complete array has to be provided. Any aspects the node has applied but are not present in the array will be removed. Conversely, any aspects in the array that the node does not have applied are added. See the link at the beginning of this page to a section about adding aspects.
The type of the node can also be changed by updating the nodeType
property, for example, to change the node
type to cm:savedquery
use the following body:
{
"nodeType":"cm:savedquery"
}
To update and set value(s) for a multi-value property first read this background information.
The POST data for a multi-value property is an array as follows:
{
"properties":
{
"acme:campaign": [
"Campaign A",
"Campaign D"
]
}
}
There’s support for setting multi-valued properties on node creation and update. Here’s a curl example of how to do it on update:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "properties": { "acme:campaign": ["Campaign A", "Campaign D"]}}' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e' | jq
In the examples above we’ve used a file, everything can obviously also be done for folders.
Add aspects to a folder or file
Adding aspects to a folder or file is a bit more complicated than just updating properties. Here is how to do it.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/updateNode
See also:
- How to update metadata - includes information about multi-value properties
- How to remove aspects
Note: It is not possible to include aspects or properties that are part of the
systemModel
(i.e. namespacesys:
) in API calls.For example, if you try and add the
sys:hidden
aspect to a node, the following error is returned:
{ “error”: { “errorKey”: “framework.exception.ApiDefault”, “statusCode”: 400, “briefSummary”: “NameSpace cannot be used by API: sys:hidden”, “stackTrace”: “For security reasons the stack trace is no longer displayed, but the property is kept for previous versions”, “descriptionURL”: “https://api-explorer.alfresco.com” } }
When you set a property on a file via the update node call the associated aspect will be applied automatically for you,
if it’s not already set on the node. Let’s take the out-of-the-box cm:effectivity
aspect for example, it has two properties
cm:from
and cm:to
. If we set one or both of these properties, then the aspect should be applied automatically.
The following HTTP PUT call is used (same as when updating metadata for a node):
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}
The folder or file node that should be updated with a new aspect is specified with the {id}
parameter, which represents
the Node Identifier. Then a body is created with all the properties belonging to the aspect.
The following call will add the cm:effectivity
aspect to the text file identified with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e
Node Identifier. The data that is passed in the PUT call looks as follows:
{
"properties": {
"cm:from":"2019-09-10T08:00:00.000+0000",
"cm:to": "2019-09-12T21:00:00.000+0000"
}
}
And here is the call:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "properties": { "cm:from": "2019-09-10T08:00:00.000+0000", "cm:to": "2019-09-12T21:00:00.000+0000"}}' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1021 100 919 100 102 4048 449 --:--:-- --:--:-- --:--:-- 4497
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-09T12:47:20.877+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
},
"parentId": "5a858591-752f-49d0-b686-e2e1a830ea8d",
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity"
],
"createdAt": "2019-09-05T08:58:24.463+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "0305fc9c-fc1d-405a-abf0-af482a9239ec",
"properties": {
"cm:from": "2019-09-10T08:00:00.000+0000",
"cm:title": "My text",
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:to": "2019-09-12T21:00:00.000+0000",
"cm:lastThumbnailModification": [
"doclib:1567673959670",
"pdf:1567673964745"
],
"cm:description": "My text document description"
}
}
}
In the returned entry we can see that the cm:effectivity
aspect has been applied to the node and the associated
properties cm:from
and cm:to
have been set accordingly. These properties are of the datetime
data type and must be
specified using the extended format defined by ISO standard 8601:2004. They are always in UTC.
What about an aspect that does not have any properties, a so called “marker” aspect, how do you add it to a folder or file? You will first use the get node metadata call to get all aspects, then you will add the “marker” aspect to this list. Then call the update node call with the new aspect list. Here is how to do this:
Get the list of aspects for the node, set fields=aspectNames
so you only get back aspects:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e?fields=aspectNames' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 144 100 144 0 0 4235 0 --:--:-- --:--:-- --:--:-- 4235
{
"entry": {
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity"
]
}
}
Add the “marker” aspect to this list:
Let’s use the following out-of-the-box aspect:
<aspect name="cm:classifiable">
<title>Classifiable</title>
</aspect>
The cm:classifiable
aspect makes it possible to apply different types of categories to a node, such as a country.
The updated aspect list that will be passed in the PUT call looks as follows:
{
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity",
"cm:classifiable"
]
}
Here is how the call looks like:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity",
"cm:classifiable"
]
}' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e' | jq
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-16T06:36:09.968+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
},
"parentId": "5a858591-752f-49d0-b686-e2e1a830ea8d",
"aspectNames": [
"rn:renditioned",
"cm:classifiable",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity"
],
"createdAt": "2019-09-05T08:58:24.463+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "0305fc9c-fc1d-405a-abf0-af482a9239ec",
"properties": {
"cm:from": "2019-09-10T08:00:00.000+0000",
"cm:title": "My text",
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:to": "2019-09-12T21:00:00.000+0000",
"cm:lastThumbnailModification": [
"doclib:1567673959670",
"pdf:1567673964745"
],
"cm:description": "My text document description"
}
}
}
We can see in the response under aspectNames
that the new cm:classifiable
aspect has been applied to the node.
Remove aspects from a folder or file
Removing aspects from a folder or file is a bit more complicated than just updating properties. Here is how to do it.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/updateNode See also:
Note: It is not possible to include aspects or properties that are part of the
systemModel
(i.e. namespacesys:
) in API calls.For example, if you try and remove the
sys:hidden
aspect from a node, the following error is returned:
{ “error”: { “errorKey”: “framework.exception.ApiDefault”, “statusCode”: 400, “briefSummary”: “NameSpace cannot be used by API: sys:hidden”, “stackTrace”: “For security reasons the stack trace is no longer displayed, but the property is kept for previous versions”, “descriptionURL”: “https://api-explorer.alfresco.com” } }
Removing an aspect from a node is similar to how you add a “marker” aspect. You first get the list of aspects currently applied to the node. Then you remove the aspect from the list. And finally you use an update node call with the updated aspect list.
To demonstrate how to remove an aspect and its properties we will assume that we have a node with id d8f561cc-e208-4c63-a316-1ea3d3a4e10e
that has the cm:effectivity
aspect applied and its properties cm:from
and cm:to
set. Here are the steps to
remove the aspect:
Get the list of aspects for the node, set fields=aspectNames
so you only get back aspects:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e?fields=aspectNames' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 144 100 144 0 0 4235 0 --:--:-- --:--:-- --:--:-- 4235
{
"entry": {
"aspectNames": [
"rn:renditioned",
"cm:classifiable",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"cm:effectivity"
]
}
}
We can see that the node has the cm:effectivity
aspect applied.
Take the aspect list and remove this aspect so you end up with the following list:
{
"aspectNames": [
"rn:renditioned",
"cm:classifiable",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification"
]
}
Use this new updated list in the update node call as follows:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{
"aspectNames": [
"rn:renditioned",
"cm:classifiable",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification"
]
}
' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e' | jq
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-16T06:57:54.854+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
},
"parentId": "5a858591-752f-49d0-b686-e2e1a830ea8d",
"aspectNames": [
"rn:renditioned",
"cm:classifiable",
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"cm:thumbnailModification"
],
"createdAt": "2019-09-05T08:58:24.463+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "0305fc9c-fc1d-405a-abf0-af482a9239ec",
"properties": {
"cm:title": "My text",
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:lastThumbnailModification": [
"doclib:1567673959670",
"pdf:1567673964745"
],
"cm:description": "My text document description"
}
}
}
As we can see in the response under aspectNames
, the new cm:effectivity
aspect is no longer applied to the node.
Note also that in the properties list the cm:to
and cm:from
properties have been removed automatically.
Get and Set permissions for a folder or file
Get and set permissions for a user or group on a folder or file node.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/updateNode
See also:
In most Alfresco solutions it’s a requirement to set permissions on folders and files for different groups and users. Groups, users, and group memberships are usually synced/imported from an LDAP directory in a production environment. It’s common to configure permissions for groups on folders so we will look at that and at the same time show also how to set permissions on nodes for users.
Note: In Content Services 6.2.2 and above changing permissions on a node with no explicit permissions (i.e. all permissions are inherited from a parent) has a time limit for the ACL propagation on children. This is needed for large node-trees where changes cannot be performed synchronously in one transaction due to resource limitations. In these cases the updates to the nodes will be scheduled for asynchronous processing which will be used for all changes that could not be completed within the set time limit. The limit can be configured by changing the
system.fixedACLs.maxTransactionTime
property in thetomcat/shared/classes/alfresco-global.properties
file. The processing is handled by thefixedACLsUpdater
job which can be scheduled to run by changing a CRON expression in the property:system.fixedACLsUpdater.cronExpression
.
Permissions are set on nodes (i.e. folders and files) by updating the metadata for the node.
The following HTTP PUT call is used:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}
The Node Identifier for the folder or file node to configure permissions for is specified with the {id}
parameter.
Then a body is created with all the data that makes up the permission update.
Here is how the PUT body looks like:
{
"permissions":
{
"isInheritanceEnabled": [true = inherit permissions from parent folder | false = don't inherit any permission settings from parent],
"locallySet": - array of permissions set locally on the folder
[
{ "authorityId": "group id or user id",
"name": "permission role - see below",
"accessStatus":"[ALLOWED = permission role is allowed| DENIED = permission role is denied]"},
...
}
]
}
}
The following list explains the practical meanings of the different high level permission roles:
- Consumer - Can read/access folders and files.
- Contributor - Consumer + permission to add folders and files.
- Editor - Consumer + permission to update folders and files.
- Collaborator - Contributor + Editor + permission to update folders and files created by other users.
- Coordinator - Full rights, similar to an admin.
There are also more low level permission roles, such as Read and Write.
If you want to add or remove locally set permissions, then you must first use the GET /nodes/{id}?include=permissions
call to get the complete set of the already locally set permissions. And then update this list of locally set permissions
to match what you want, you cannot just set a new permission and then expect it to be merged with existing local permissions
on the server side, this have to be done on the client side.
Let’s assume we have a folder called Engineering
that is created directly under /Company Home
. To get currently set
local permissions for this folder use the following call:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-?relativePath=/Engineering&include=permissions' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 643 0 643 0 0 3632 0 --:--:-- --:--:-- --:--:-- 3653
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable"
],
"createdAt": "2019-12-02T07:54:35.401+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-12-02T07:54:35.401+0000",
"permissions": {
"inherited": [
{
"authorityId": "GROUP_EVERYONE",
"name": "Consumer",
"accessStatus": "ALLOWED"
}
],
"settable": [
"Contributor",
"Collaborator",
"Coordinator",
"Editor",
"Consumer"
],
"isInheritanceEnabled": true
},
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Engineering",
"id": "d9cb3cf4-d7fc-4240-acd4-2483c4ca932c",
"nodeType": "cm:folder",
"parentId": "32d4ff8a-7f5e-4c90-99d5-e4ac54855b35"
}
}
The permissions
property contains all the information about what permissions that have been set locally on the folder
and what permissions that have been inherited from parent folders. In this case there are no locally set permissions,
just the inherited Consumer role permission for group EVERYONE.
When we get the permissions for a node we also get back a list of permission roles, in the settable
property, that can
be set on the node (depends on the node type).
Let’s also look at the permissions for a public Share site, we can get the document library node information for the
out-of-the-box site with id swsdp
as follows:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-?relativePath=/Sites/swsdp/documentLibrary&include=permissions' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1665 0 1665 0 0 108k 0 --:--:-- --:--:-- --:--:-- 116k
{
"entry": {
"isFile": false,
"createdByUser": {
"id": "mjackson",
"displayName": "Mike Jackson"
},
"modifiedAt": "2011-02-15T20:16:28.292+0000",
"nodeType": "cm:folder",
"parentId": "b4cff62a-664d-4d45-9302-98723eac1319",
"aspectNames": [
"cm:tagscope",
"st:siteContainer",
"cm:ownable",
"cm:titled",
"cm:auditable"
],
"createdAt": "2011-02-15T20:16:28.292+0000",
"isFolder": true,
"permissions": {
"inherited": [
{
"authorityId": "GROUP_site_swsdp_SiteContributor",
"name": "SiteContributor",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_site_swsdp_SiteConsumer",
"name": "SiteConsumer",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_site_swsdp_SiteManager",
"name": "SiteManager",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_site_swsdp_SiteCollaborator",
"name": "SiteCollaborator",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_EVERYONE",
"name": "SiteConsumer",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_EVERYONE",
"name": "ReadPermissions",
"accessStatus": "ALLOWED"
}
],
"settable": [
"Contributor",
"Collaborator",
"Coordinator",
"Editor",
"Consumer"
],
"isInheritanceEnabled": true
},
"modifiedByUser": {
"id": "mjackson",
"displayName": "Mike Jackson"
},
"name": "documentLibrary",
"id": "8f2105b4-daaf-4874-9e8a-2152569d109b",
"properties": {
"cm:tagScopeCache": {
"contentUrl": "store://2019/1/16/15/53/ff4d2006-ec20-4877-b6a3-acc5c44d6410.bin",
"mimetype": "text/plain",
"size": 0,
"encoding": "UTF-8",
"locale": "en_GB",
"id": 155,
"infoUrl": "contentUrl=store://2019/1/16/15/53/ff4d2006-ec20-4877-b6a3-acc5c44d6410.bin|mimetype=text/plain|size=0|encoding=UTF-8|locale=en_GB_"
},
"cm:tagScopeSummary": [],
"cm:owner": {
"id": "admin",
"displayName": "Administrator"
},
"st:componentId": "documentLibrary",
"cm:description": "Document Library"
}
}
}
Here we have a few more inherited permission settings that have been automatically set up when the site was created.
When you create a site four new groups are also created automatically and default permissions are set up for those.
Note that the permission role names (SiteConsumer
, SiteContributor
, SiteCollaborator
, and SiteManager
) are
slightly different from the ones used when setting local permissions (i.e. settable
).
Now, let’s look at an example of how to update the permissions for the Engineering
folder node. It should be straight
forward as we know that this folder doesn’t have any locally set permissions.
We will use the following POST body to set new permissions for a group and a user:
{
"permissions":
{
"isInheritanceEnabled": true,
"locallySet":
[
{"authorityId": "GROUP_engineering", "name": "Collaborator", "accessStatus":"ALLOWED"},
{"authorityId": "test", "name": "Contributor", "accessStatus":"ALLOWED"}
]
}
}
In this case we are giving a group with identifier engineering
Collaborator permissions on the /Company Home/Engineering
folder. Note that group identifiers have to be prefixed with GROUP_
. At the same time we also give the user identified
with username test
Contributor permissions on the Engineering
folder.
And here is the call, note that we cannot use the relativePath
parameter here, instead we have to use the Node Identifier
for the Engineering
folder, which we know from the GET call above:
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "permissions": { "isInheritanceEnabled": true, "locallySet": [ {"authorityId": "GROUP_engineering", "name": "Collaborator", "accessStatus":"ALLOWED"},{"authorityId": "test", "name": "Contributor", "accessStatus":"ALLOWED"} ] } }' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d9cb3cf4-d7fc-4240-acd4-2483c4ca932c?include=permissions' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1099 0 869 100 230 39500 10454 --:--:-- --:--:-- --:--:-- 52333
{
"entry": {
"aspectNames": [
"cm:titled",
"cm:auditable"
],
"createdAt": "2019-12-02T08:50:11.922+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-12-02T09:20:03.409+0000",
"permissions": {
"inherited": [
{
"authorityId": "GROUP_EVERYONE",
"name": "Read",
"accessStatus": "ALLOWED"
},
{
"authorityId": "guest",
"name": "Read",
"accessStatus": "ALLOWED"
}
],
"locallySet": [
{
"authorityId": "test",
"name": "Contributor",
"accessStatus": "ALLOWED"
},
{
"authorityId": "GROUP_engineering",
"name": "Collaborator",
"accessStatus": "ALLOWED"
}
],
"settable": [
"Contributor",
"Collaborator",
"Coordinator",
"Editor",
"Consumer"
],
"isInheritanceEnabled": true
},
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Engineering",
"id": "d0ec1a36-0bda-40b9-8602-804b787f800e",
"nodeType": "cm:folder",
"parentId": "209d2313-f9e8-4075-b778-08d9d4725cd2"
}
}
Note that by using the /nodes/d9cb3cf4-d7fc-4240-acd4-2483c4ca932c?include=permissions
PUT call the new permission
configuration (i.e. the permissions
property) is returned in the response, so we can see immediately that the new
local permissions have been set properly in the locallySet
property.
If you now wanted to update the locally set permissions for the “Engineering” folder again, then you would have to
include the permissions we just set, and if you don’t know them use the GET /nodes/-root-?relativePath=/Engineering&include=permissions
call to fetch them, otherwise they would be removed:
{
"permissions":
{
"isInheritanceEnabled": true,
"locallySet":
[
{"authorityId": "GROUP_engineering", "name": "Collaborator", "accessStatus":"ALLOWED"}, -- permission already set that we want to keep
{"authorityId": "test", "name": "Contributor", "accessStatus":"ALLOWED"}, -- permission already set that we want to keep
{"authorityId": more new permission settings here...},
...
]
}
}
If you wanted to remove all locally set permissions for a node, then you could use the following body in the call:
{
"permissions": {
"isInheritanceEnabled": true,
"locallySet": [ ]
}
}
Working with relationships between folders/files
Setting up relationships, referred to as associations, between different types of nodes is useful when modelling a specific domain.
Introduction
There are quite a few API calls involved in managing associations between nodes in the repository:
API Call | Description | API Explorer |
---|---|---|
GET nodes/{parentId}/children | List the parent-to-primary-child associations (i.e. cm:contains ) for a parent node. |
http://localhost:8080/api-explorer/#!/nodes/listNodeChildren |
POST nodes/{parentId}/children | Create a new parent-to-primary-child association (i.e. cm:contains ) for a parent node. |
http://localhost:8080/api-explorer/#!/nodes/createNode |
GET nodes/{parentId}/secondary-children | List the parent-to-secondary-child associations for a parent node. | http://localhost:8080/api-explorer/#!/nodes/listSecondaryChildren |
GET nodes/{childId}/parents | Get a list of parent nodes that are associated with a child node. | http://localhost:8080/api-explorer/#!/nodes/listParents |
POST nodes/{parentId}/secondary-children | Create a new parent-to-secondary-child association for a parent node. | http://localhost:8080/api-explorer/#!/nodes/createSecondaryChildAssociation |
DELETE /nodes/{parentId}/secondary-children/{childId} | Delete a parent-to-secondary-child association. | http://localhost:8080/api-explorer/#!/nodes/deleteSecondaryChildAssociation |
GET nodes/{sourceId}/targets | List the peer-to-peer associations for a source node. | http://localhost:8080/api-explorer/#!/nodes/listTargetAssociations |
GET nodes/{targetId}/sources | Get a list of source nodes that are associated with a target node. | http://localhost:8080/api-explorer/#!/nodes/listSourceAssociations |
POST nodes/{sourceId}/targets | Create a new peer-to-peer association for a source node. | http://localhost:8080/api-explorer/#!/nodes/createAssociation |
DELETE /nodes/{sourceId}/targets/{targetId} | Delete a peer-to-peer association. | http://localhost:8080/api-explorer/#!/nodes/deleteSecondaryChildAssociation |
When working with content, it’s important to be able to set up relationships such as folder to files and file to file.
These relationships are referred to as associations in the Alfresco content model. Out of the box you have already worked
with a folder to folders and files association, which is used to build the folder hierarchy that you see in the UI clients.
This association is called cm:contains
and is part of the cm:folder
type:
<type name="cm:folder">
<title>Folder</title>
<parent>cm:cmobject</parent>
<archive>true</archive>
<associations>
<child-association name="cm:contains">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>sys:base</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
<duplicate>false</duplicate>
<propagateTimestamps>true</propagateTimestamps>
</child-association>
</associations>
</type>
This association is of the type child-association
, which means that if you delete the folder the contained folders and
files will also be automatically deleted. There is also a peer-to-peer association type that you can see an example of
in the out-of-the-box cm:person
type definition:
<type name="cm:person">
<title>Person</title>
<parent>cm:authority</parent>
<properties>
...
</properties>
<associations>
<association name="cm:avatar">
<source>
<role>cm:avatarOf</role>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:content</class>
<role>cm:hasAvatar</role>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
</associations>
</type>
The peer-to-peer association type is called just association
. If you delete a person node the associated avatar will
not be automatically deleted.
An association is built up of a source
and a target
definition, and the source is basically the class (i.e. type or
aspect) that defines the association. You can use the mandatory
and many
properties to define the cardinality for
the association.
When you work with the cm:contains
association type you set up what is referred to as primary parent-child associations.
This is what happens when you upload a file or create a folder. There is also the possibility to work with other types
of child associations, they are then referred to as secondary child associations. We will have a look at them now.
Introduction to the FDK content model
To be able to show how to use secondary child associations and peer-to-peer associations we will use a custom content model. For those of you that have been with Alfresco for a while you might remember the Forms Development Kit (FDK) custom model. It contains a Gadget type definition with some useful association definitions that we can use for demonstration purposes:
The fdk:gadget
type extends cm:content
, which represents a file, so it can be used to represent for example a review
of a gadget in a magazine. We can also see a number of associations defined for fdk:gadget
representing things like
gadget pictures (fdk:images
), gadget company (fdk:company
), gadget contact person (fdk:contact
), gadget press
release (fdk:pressRelease
), and gadget reviews (fdk:reviews
). Note that you cannot create an fdk:gadget
without
supplying a picture of it as the fdk:images
association has been defined with cardinality 1..*
, meaning one is mandatory.
For reference, the fdk:gadget
type definition looks like this:
<type name="fdk:gadget">
<parent>cm:content</parent>
<properties>
<property name="fdk:make">
<type>d:text</type>
<mandatory>true</mandatory>
</property>
<property name="fdk:model">
<type>d:text</type>
<mandatory>true</mandatory>
</property>
<property name="fdk:summary">
<type>d:text</type>
<mandatory>true</mandatory>
<constraints>
<constraint ref="fdk:summary" />
</constraints>
</property>
<property name="fdk:type">
<type>d:text</type>
<constraints>
<constraint ref="fdk:type" />
</constraints>
</property>
<property name="fdk:subType">
<type>d:text</type>
<constraints>
<constraint ref="fdk:subType" />
</constraints>
</property>
<property name="fdk:rrp">
<type>d:float</type>
</property>
<property name="fdk:releaseDate">
<type>d:datetime</type>
</property>
<property name="fdk:endOfLifeDate">
<type>d:date</type>
</property>
<property name="fdk:retailers">
<type>d:text</type>
<multiple>true</multiple>
</property>
<property name="fdk:rating">
<type>d:int</type>
<constraints>
<constraint ref="fdk:percentage" />
</constraints>
</property>
</properties>
<associations>
<association name="fdk:contact">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:person</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
<association name="fdk:reviews">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
<association name="fdk:company">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>fdk:company</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
<child-association name="fdk:pressRelease">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</child-association>
<child-association name="fdk:images">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>true</mandatory>
<many>true</many>
</target>
</child-association>
</associations>
</type>
We have two child associations defined for the image and press release files, which will be automatically deleted when you delete the gadget file. Then we have the company, contact, and reviews associations that are peer-to-peer associations and these will not be deleted when you deleted the gadget file, which make sense as a company and contact person can be related to multiple gadgets.
Before we move on and use the FDK content model to demonstrate associations we need to install it into our Alfresco system. It’s assumed that you are using Alfresco SDK.
Installing the FDK content model into an Alfresco SDK AIO project
Download the Form Development Kit (FDK) Source JAR
Download the FDK Source code from Alfresco artifacts repository (Nexus).
Copy the FDK content model files into the SDK project
Change the extension name for the FDK JAR file so its a ZIP file instead. Then open and extract its content. Now copy
both the fdk-model.xml
file and the fdk-model.properties
file into the aio/aio-platform-docker/src/main/docker
AIO SDK directory.
Bootstrap FDK content model
Update the acs61-aio/acs61-aio-platform/src/main/resources/alfresco/module/acs61-aio-platform/context/bootstrap-context.xml
file so it also bootstraps the FDK content model, the dictionary bootstrapper bean should now look something like this:
<bean id="acs61-aio-platform.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
<property name="models">
<list>
<value>alfresco/module/${project.artifactId}/model/content-model.xml</value>
<value>alfresco/module/${project.artifactId}/model/workflow-model.xml</value>
<value>alfresco/extension/fdk-model.xml</value>
</list>
</property>
<property name="labels">
<list>
<!-- Bootstrap Resource Bundles for the content model types, aspects, properties etc -->
<value>alfresco/module/${project.artifactId}/messages/content-model</value>
<value>alfresco/extension/fdk-model</value>
</list>
</property>
</bean>
Open up the platform/repository Docker file and add the command to copy the FDK content model files into an alfresco/extension
directory
The platform (repository) Docker file is located in the aio/aio-platform-docker/src/main/docker
AIO SDK directory.
Add the following COPY command at the end of this file:
...
# Copy in the FDK content model for association/relationship example
COPY fdk-model.xml fdk-model.properties $TOMCAT_DIR/shared/classes/alfresco/extension/
What this Dockerfile will do is build a custom Repository Docker image that is based on the out-of-the-box Alfresco Repository Docker image that you are using. After it has copied in all the extensions, config files, license etc it will finish by copying in the fdk-model.xml and fdk-model.properties files into the tomcat/shared/classes/alfresco/extension directory where they will be picked up and deployed.
Restart the platform/repository container
We have changed only the platform/repository, so it is enough to just restart this container:
acs61-aio mbergljung$ ./run.sh reload_acs
Killing docker_acs61-aio-acs_1 ... done
Going to remove docker_acs61-aio-acs_1
Removing docker_acs61-aio-acs_1 ... done
...
Note. this does not remove any content or metadata.
Now that the FDK content model is available in our Alfresco installation we can move on and try out associations.
Upload files needed when creating associations
As we learned, a gadget file cannot be created without an associated picture. If we tried we would get an error such as:
{
"error": {
"errorKey": "framework.exception.ApiDefault",
"statusCode": 422,
"briefSummary": "10190002 Found 1 integrity violations:\nThe association child multiplicity has been violated:
...
So we need to upload a gadget picture first.
Before uploading any files create a folder called My Gadgets under /Company Home (i.e. root). To do this we POST
to the /nodes/{id}/children
URL:
curl -H "Content-Type: application/json" -d '{"name":"My Gadgets","nodeType":"cm:folder"}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
Now we can upload the gadget picture, prepare a PNG somewhere with the filename gadget-picture.png
and then upload it as follows:
$ curl -X POST -F filedata=@gadget-picture.png -F "name=gadget-picture.png" -F "nodeType=cm:content" -F "relativePath=My Gadgets" -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43532 0 701 100 42831 1070 65390 --:--:-- --:--:-- --:--:-- 66359
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T09:32:53.121+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "image/png",
"mimeTypeName": "PNG Image",
"sizeInBytes": 42303,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author",
"exif:exif"
],
"createdAt": "2019-10-21T09:32:53.121+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "gadget-picture.png",
"id": "b8dc45cd-6828-41df-a908-e0fa8dbbd96b",
"properties": {
"exif:pixelYDimension": 340,
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"exif:pixelXDimension": 340
}
}
}
Make a note about the Node Identifier for the gadget-picture.png
file as we are going to need it in a bit. While we
are at it we can also prepare two of the other associations that we can specify when we create a gadget node. Let’s
first upload a gadget review. Create a text file called gadget-review.txt
and upload it as follows to the My Gadgets
folder:
$ curl -X POST -F filedata=@gadget-review.txt -F "name=gadget-review.txt" -F "nodeType=cm:content" -F "relativePath=My Gadgets" -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1212 0 638 100 574 2345 2110 --:--:-- --:--:-- --:--:-- 4472
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T09:42:48.727+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 47,
"encoding": "ISO-8859-1"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:auditable",
"cm:author"
],
"createdAt": "2019-10-21T09:42:48.727+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "gadget-review.txt",
"id": "581baebd-3814-4cd0-884a-3179f2dac0ac",
"properties": {
"cm:versionLabel": "1.0",
"cm:versionType": "MAJOR"
}
}
}
Make a note about the Node Identifier for the gadget-review.txt
file as we are going to need it too later. Next let’s
also create a company node:
$ curl -H "Content-Type: application/json" -d '{"relativePath":"My Gadgets", "name":"Cool Gadgets Inc","nodeType":"fdk:company", "properties": {"fdk:email":"info@coolgadgets.com","fdk:url":"www.coolgadgets.com","fdk:city":"London"}}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 844 0 659 100 185 9835 2761 --:--:-- --:--:-- --:--:-- 12597
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:01:55.279+0000",
"nodeType": "fdk:company",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 0,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"aspectNames": [
"cm:auditable"
],
"createdAt": "2019-10-21T10:01:55.279+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Cool Gadgets Inc",
"id": "5bae662a-b450-49c0-8f40-8ba2046aa423",
"properties": {
"fdk:email": "info@coolgadgets.com",
"fdk:city": "London",
"fdk:url": "www.coolgadgets.com"
}
}
}
Warning: If you get an error response saying “fdk:company isn’t a valid QName” it means you haven’t installed the FDK content model properly.
Create a node with associations using the FDK content model
Now we’re finally ready to create our gadget node. We can specify the child associations using the secondaryChildren
property and the peer associations using the targets
property. Use the node IDs for the gadget image, review, and
company that we created earlier on. We POST to /nodes/{id}/children
as usual:
$ curl -H "Content-Type: application/json" -d '{"relativePath":"My Gadgets","name":"My Gadget","nodeType":"fdk:gadget", "secondaryChildren": [ {"childId":"b8dc45cd-6828-41df-a908-e0fa8dbbd96b","assocType":"fdk:images"}], "targets": [ {"targetId":"581baebd-3814-4cd0-884a-3179f2dac0ac","assocType":"fdk:reviews"},{"targetId":"5bae662a-b450-49c0-8f40-8ba2046aa423","assocType":"fdk:company"}]}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 893 0 549 100 344 3496 2191 --:--:-- --:--:-- --:--:-- 5687
{
"entry": {
"aspectNames": [
"cm:auditable"
],
"createdAt": "2019-10-21T10:08:09.561+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:08:09.561+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "My Gadget",
"id": "74351ea7-8c72-44e4-829c-7d606a8682c7",
"nodeType": "fdk:gadget",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 0,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
}
Here we are creating a contentless file with three associations. We cannot actually see in the response that these associations have been created. There is an endpoint we can use to return the associations for a node.
List associations for a node
To get a list of the targets (peer associations) we created make a GET call to the following URL:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{sourceId}/targets
The {sourceId}
represents the node identifier for the node we want to list peer associations for. To list associations
for our newly created gadget with the node id 74351ea7-8c72-44e4-829c-7d606a8682c7 make a call as follows:
$ curl -X GET -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/targets | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1226 0 1226 0 0 15325 0 --:--:-- --:--:-- --:--:-- 15325
{
"list": {
"pagination": {
"count": 2,
"hasMoreItems": false,
"totalItems": 2,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-10-21T09:42:48.727+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T09:42:48.727+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "gadget-review.txt",
"association": {
"assocType": "fdk:reviews"
},
"id": "581baebd-3814-4cd0-884a-3179f2dac0ac",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 47,
"encoding": "ISO-8859-1"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
},
{
"entry": {
"createdAt": "2019-10-21T10:01:55.279+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:01:55.279+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Cool Gadgets Inc",
"association": {
"assocType": "fdk:company"
},
"id": "5bae662a-b450-49c0-8f40-8ba2046aa423",
"nodeType": "fdk:company",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 0,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
}
]
}
}
We can see here that there exists two peer associations for the gadget node, one for the review and one for the company. If you remove the gadget node these target nodes will not be deleted automatically.
We can also combine some of the techniques we’ve learned in other sections of the ReST API User Guide and just request
the fdk:reviews
association and show the path and properties of the target node using the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{sourceId}/targets?include=properties,path&where=(assocType='{association type}')
To get all the reviews for the gadget make the call as follows:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/targets?where=(assocType%3D'fdk%3Areviews')&include=path%2C%20properties' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1064 0 1064 0 0 50666 0 --:--:-- --:--:-- --:--:-- 53200
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T09:42:48.727+0000",
"association": {
"assocType": "fdk:reviews"
},
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 47,
"encoding": "ISO-8859-1"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"createdAt": "2019-10-21T09:42:48.727+0000",
"path": {
"name": "/Company Home/My Gadgets",
"isComplete": true,
"elements": [
{
"id": "695c2c56-3ba0-4539-b301-12bd9bb47712",
"name": "Company Home",
"nodeType": "cm:folder",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
]
},
{
"id": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"name": "My Gadgets",
"nodeType": "cm:folder",
"aspectNames": [
"cm:auditable"
]
}
]
},
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "gadget-review.txt",
"id": "581baebd-3814-4cd0-884a-3179f2dac0ac",
"properties": {
"cm:versionLabel": "1.0",
"cm:versionType": "MAJOR"
}
}
}
]
}
}
We can retrieve the list of secondary child associations by using the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{sourceId}/secondary-children
Here is how that looks like:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/secondary-children' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 671 0 671 0 0 13420 0 --:--:-- --:--:-- --:--:-- 13420
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-10-21T09:32:53.121+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:08:09.614+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "gadget-picture.png",
"association": {
"isPrimary": false,
"assocType": "fdk:images"
},
"id": "b8dc45cd-6828-41df-a908-e0fa8dbbd96b",
"nodeType": "cm:content",
"content": {
"mimeType": "image/png",
"mimeTypeName": "PNG Image",
"sizeInBytes": 42303,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
}
]
}
}
The response is similar to the previous example except we’re seeing the fdk:images
child association.
What if we want to go in the other direction and see what links to a particular node, for peer associations we can use the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{targetId}/sources
In this case the {targetId}
would be the node identifier of the target node in the association. For example, to get
anything linking to the gadget review text file (581baebd-3814-4cd0-884a-3179f2dac0ac) use the following GET call:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/581baebd-3814-4cd0-884a-3179f2dac0ac/sources' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 673 0 673 0 0 9219 0 --:--:-- --:--:-- --:--:-- 9219
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-10-21T10:08:09.561+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:08:09.561+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "My Gadget",
"association": {
"assocType": "fdk:reviews"
},
"id": "74351ea7-8c72-44e4-829c-7d606a8682c7",
"nodeType": "fdk:gadget",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 0,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
}
]
}
}
We should see that the “My Gadget” node is linked to the review text file.
To see parents for child associations we use a GET call to the following URL:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{childId}/parents
For example, to see the parents of the gadget picture file (b8dc45cd-6828-41df-a908-e0fa8dbbd96b) we uploaded earlier, we should see 2 parents, the “My Gadget” node and the “My Gadgets” folder where the image itself was uploaded:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/b8dc45cd-6828-41df-a908-e0fa8dbbd96b/parents' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1139 0 1139 0 0 33500 0 --:--:-- --:--:-- --:--:-- 33500
{
"list": {
"pagination": {
"count": 2,
"hasMoreItems": false,
"totalItems": 2,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-10-21T10:08:09.561+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:08:09.561+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "My Gadget",
"association": {
"isPrimary": false,
"assocType": "fdk:images"
},
"id": "74351ea7-8c72-44e4-829c-7d606a8682c7",
"nodeType": "fdk:gadget",
"content": {
"mimeType": "application/octet-stream",
"mimeTypeName": "Binary File (Octet Stream)",
"sizeInBytes": 0,
"encoding": "UTF-8"
},
"parentId": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3"
}
},
{
"entry": {
"createdAt": "2019-10-21T09:30:41.096+0000",
"isFolder": true,
"isFile": false,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-21T10:08:09.692+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "My Gadgets",
"association": {
"isPrimary": true,
"assocType": "cm:contains"
},
"id": "e2bdf9a3-ef31-469f-aeb4-07f2083961b3",
"nodeType": "cm:folder",
"parentId": "695c2c56-3ba0-4539-b301-12bd9bb47712"
}
}
]
}
}
Create an association for a node that exists
It’s also possible to create associations on nodes that already exist, let’s add some more to our “My Gadget” fdk:gadget
node. Before we do that though upload another image to the “My Gadgets” folder and some more content to represent another
review, do it via ReST API or UI.
To create another child association we use the same URL we used earlier to retrieve the list of secondary child associations
(nodes/{sourceId}/secondary-children
) except this time we POST to it. Sending the body below will create another
child association to the 2nd image with node ID 3e76d633-5dad-475e-bc9d-26a33ec64e11:
{
"childId": "3e76d633-5dad-475e-bc9d-26a33ec64e11",
"assocType": "fdk:images"
}
Here is how the call looks like:
$ curl -H "Content-Type: application/json" -d '{"childId":"3e76d633-5dad-475e-bc9d-26a33ec64e11", "assocType":"fdk:images"}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/secondary-children | jq
100 161 0 85 100 76 1328 1187 --:--:-- --:--:-- --:--:-- 2515
{
"entry": {
"assocType": "fdk:images",
"childId": "3e76d633-5dad-475e-bc9d-26a33ec64e11"
}
}
We can do the same thing to create another peer association by POSTing the following data to nodes/{sourceId}/targets
(second review node ID = 80983f1e-ef4e-4285-bd08-94a343923192):
{
"targetId": "80983f1e-ef4e-4285-bd08-94a343923192",
"assocType": "fdk:reviews"
}
Here is how the call looks like:
$ curl -H "Content-Type: application/json" -d '{"targetId":"80983f1e-ef4e-4285-bd08-94a343923192", "assocType":"fdk:reviews"}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/targets | jq
100 161 0 85 100 76 1328 1187 --:--:-- --:--:-- --:--:-- 2515
{
"entry": {
"targetId": "80983f1e-ef4e-4285-bd08-94a343923192",
"assocType": "fdk:reviews"
}
}
If you now do a GET on the same URLs you should now see two fdk:images
child associations and three peer associations,
two of type fdk:reviews
and one of type fdk:company
.
Alfresco has a feature called multi-filing, this is where a node can appear in multiple folders, think of it as a unix symbolic link. This feature has been available via the CMIS API for a long time but it’s now exposed via the v1 ReST API too.
When we navigate around the repository we’re actually following the cm:contains
child association, to make a node appear
in multiple folders we can create a secondary child association from the folder to the node. To make the review text we
uploaded in the beginning also appear in the /Company Home folder POST the following body to nodes/{companyHomeFolderId}/secondary-children
{
"childId": "{reviewTextId}",
"assocType": "cm:contains"
}
To find out the node ID for the company home folder you can go to Admin Tools > Node Browser in Share.
Here is how this call looks like:
$ curl -H "Content-Type: application/json" -d '{"childId":"581baebd-3814-4cd0-884a-3179f2dac0ac", "assocType":"cm:contains"}' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/695c2c56-3ba0-4539-b301-12bd9bb47712/secondary-children | jq
100 161 0 85 100 76 1328 1187 --:--:-- --:--:-- --:--:-- 2515
{
"entry": {
"assocType": "cm:contains",
"childId": "581baebd-3814-4cd0-884a-3179f2dac0ac"
}
}
The review file now has two parent folders but only one is the primary parent. If we use the Share UI and double click on the review file in the Company Home folder a details page is displayed for the file with breadcrumbs showing the My Gadget folder as the parent because it’s the primary parent folder.
If we request a listing of the Company Home folder secondary children using:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{companyHomeId}/secondary-children?include=association
We can find out if a child association is primary or not.
By asking for the association information to be included we can see that the gadget-review.txt
node is a secondary
child association via the isPrimary
flag, this allows clients to handle these “linked” nodes differently
(i.e. restrict deletion).
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/695c2c56-3ba0-4539-b301-12bd9bb47712/secondary-children?include=association' | jq
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
...
"name": "gadget-review.txt",
"association": {
"isPrimary": false,
"assocType": "cm:contains"
},
...
}
}
]
}
}
Deleting associations
The last thing to cover is deleting associations, let’s start by removing the second review from our fdk:gadget
node.
To do this we send a DELETE request to:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{gadgetId}/targets/{secondReviewTextId}?assocType=fdk:reviews
Here’s how this looks like:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/targets/80983f1e-ef4e-4285-bd08-94a343923192?assocType=fdk:reviews' | jq
We can do the same thing for child associations using DELETE. To remove the child association between the fdk:gadget
and the second image we uploaded use the following URL:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{gadgetId}/secondary-children/{secondImageId}?assocType=fdk:images
Here is how that looks like:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/74351ea7-8c72-44e4-829c-7d606a8682c7/secondary-children/3e76d633-5dad-475e-bc9d-26a33ec64e11?assocType=fdk:images' | jq
Although it’s not mandatory it’s important to specify the assocType
query parameter, this defines which type of
associations to remove, if we omit this parameter ALL associations (peer or child depending on the URL used) between the
two nodes are removed.
Manage comments for a folder or file
Get, add, update, and remove comments for a folder or file node.
API Explorer URLs:
- https://api-explorer.alfresco.com/api-explorer/#!/comments/listComments
- https://api-explorer.alfresco.com/api-explorer/#!/comments/createComment
- https://api-explorer.alfresco.com/api-explorer/#!/comments/deleteComment
- https://api-explorer.alfresco.com/api-explorer/#!/comments/updateComment
See also: How to update a folder or file
Add a comment
You can add comments to both folders and files. This is part of the collaboration features that exists around nodes. There is also like/unlike and tagging of nodes.
Comments are added to nodes with the following POST call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/comments
The Node Identifier for the folder or file node to add the comment to is specified with the {id}
parameter. Then a
POST body is created with the comment as follows:
{
"content": "This is a comment"
}
It’s possible to add more than one comment at a time as follows:
[
{
"content": "This is a comment"
},
{
"content": "This is another comment"
}
]
Let’s look at an example of how to add a comment to a file (it would be done in the same way for a folder).
Let’s assume we have a file with the 7279b5c5-da55-4e98-8b12-72d33b90c810 Node Identifier, the call would then look like this:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "content": "This is a comment" }' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/comments' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 544 0 510 100 34 717 47 --:--:-- --:--:-- --:--:-* 764
{
"entry": {
"createdAt": "2019-12-03T09:32:54.803+0000",
"createdBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"edited": false,
"modifiedAt": "2019-12-03T09:32:54.803+0000",
"canEdit": true,
"modifiedBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"canDelete": true,
"id": "cace6ed3-1e57-4690-86eb-d9bce01257cf",
"content": "This is a comment"
}
}
The response contains more information about the added comment, such as its Node Identifier (i.e. id
).
Listing comments
To list all the comments for a file (or folder) we can use the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/comments
The Node Identifier for the folder or file node to get the comments for is specified with the {id}
parameter.
As an example we will get all the comments for the file we just added a comment to:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/comments' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1151 0 1151 0 0 7829 0 --:--:-- --:--:-- --:--:-- 7829
{
"list": {
"pagination": {
"count": 2,
"hasMoreItems": false,
"totalItems": 2,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-12-03T09:32:54.803+0000",
"createdBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"edited": false,
"modifiedAt": "2019-12-03T09:32:54.803+0000",
"canEdit": true,
"modifiedBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"canDelete": true,
"id": "cace6ed3-1e57-4690-86eb-d9bce01257cf",
"content": "This is a comment"
}
},
{
"entry": {
"createdAt": "2019-12-02T13:31:54.693+0000",
"createdBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"edited": false,
"modifiedAt": "2019-12-02T13:31:54.693+0000",
"canEdit": true,
"modifiedBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"canDelete": true,
"id": "7eb1a859-6c00-49c0-9eec-36c8f45db88d",
"content": "This is a comment on a file"
}
}
]
}
}
The response is constructed in the usual way for lists, with a pagination
section at the start and then entries
.
Each comment has a canEdit
and canDelete
property, this can be used to determine whether the current user has the
permission to edit or delete that comment, respectively.
We can see that the file has two comments. There is a lot of information returned for each comment. We can make it a
bit more succinct and save bandwidth by using the fields
parameter, let’s return only the date the comment was made
and the content of it:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/comments?fields=createdAt,content' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 299 0 299 0 0 5245 0 --:--:-- --:--:-- --:--:-- 5155
{
"list": {
"pagination": {
"count": 2,
"hasMoreItems": false,
"totalItems": 2,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-12-03T09:32:54.803+0000",
"content": "This is a comment"
}
},
{
"entry": {
"createdAt": "2019-12-02T13:31:54.693+0000",
"content": "This is a comment on a file"
}
}
]
}
}
That’s better.
Update a comment
Now, what if you wanted to update a comment. For this we can use the following PUT call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/comments/{commentId}
The Node Identifier for the folder or file node to update the comment for is specified with the {id}
parameter. And
the comment we want to update is specified with the {commentId}
parameter.
The PUT body contains the new comment text:
{
"content": "Updated comment text"
}
As an example we will update the latest comment for the file, which has the cace6ed3-1e57-4690-86eb-d9bce01257cf Node Identifier (can be seen in the first listing of comments above):
$ curl -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "content": "Updated comment text" }' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/comments/cace6ed3-1e57-4690-86eb-d9bce01257cf' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 549 0 512 100 37 1706 123 --:--:-- --:--:-- --:--:-- 1823
{
"entry": {
"createdAt": "2019-12-03T09:32:54.803+0000",
"createdBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"edited": true,
"modifiedAt": "2019-12-03T09:54:10.891+0000",
"canEdit": true,
"modifiedBy": {
"enabled": true,
"firstName": "Administrator",
"email": "admin@alfresco.com",
"emailNotificationsEnabled": true,
"company": {},
"id": "admin"
},
"canDelete": true,
"id": "cace6ed3-1e57-4690-86eb-d9bce01257cf",
"content": "Updated comment text"
}
}
The response contains all the information about the updated comment.
Delete a comment
The last thing you would most likely want to do with comments is to delete them. We can do that with the following DELETE call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/comments/{commentId}
The Node Identifier for the folder or file node to have its comment deleted is specified with the {id}
parameter.
And the comment we want to delete is specified with the {commentId}
parameter.
As an example we will delete the latest comment for the file, which has the cace6ed3-1e57-4690-86eb-d9bce01257cf Node Identifier:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/comments/cace6ed3-1e57-4690-86eb-d9bce01257cf' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-* 0
Manage tags for a folder or file
Get, add, and remove tags for a folder or file node.
API Explorer URLs:
- https://localhost:8080/api-explorer/#!/tags/listTagsForNode
- https://localhost:8080/api-explorer/#!/tags/createTagForNode
- https://localhost:8080/api-explorer/#!/tags/deleteTagFromNode
See also:
Add a tag
You can add tags to both folders and files. This is part of the collaboration features that exists around nodes. There is also like/unlike and commenting on nodes.
Tags are added to nodes with the following POST call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/tags
The Node Identifier for the folder or file node to add the tag to is specified with the {id}
parameter. Then a POST
body is created with the comment as follows:
{
"tag": "project-x"
}
It’s possible to add more than one tag at a time as follows:
[
{
"tag": "project-x"
},
{
"tag": "meeting"
}
]
Let’s look at an example of how to add a tag to a file (it would be done in the same way for a folder).
Let’s assume we have a file with the 7279b5c5-da55-4e98-8b12-72d33b90c810 Node Identifier, the call would then look like this:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{ "tag": "project-x" }' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/tags' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 95 0 73 100 22 182 55 --:--:-- --:--:-- --:--:-* 238
{
"entry": {
"tag": "project-x",
"id": "a427120f-a679-43e6-bcc5-4b0be1f91ea3"
}
}
The response contains the tag identifier (i.e. id
) together with the tag we just added to the node.
Listing tags
To list all the tags for a file (or folder) we can use the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/tags
The Node Identifier for the folder or file node to get the tags for is specified with the {id}
parameter.
As an example we will get all the tags for the file we just added a comment to:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/tags' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 186 0 186 0 0 5470 0 --:--:-- --:--:-- --:--:-- 5470
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"tag": "project-x",
"id": "a427120f-a679-43e6-bcc5-4b0be1f91ea3"
}
}
]
}
}
The response is constructed in the usual way for lists, with a pagination
section at the start and then entries
.
We can see that the file has one tag. If you just wanted to return the tag text and leave out the tag id, then we can
use the fields
parameter as follows:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/tags?fields=tags' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 142 0 142 0 0 10142 0 --:--:-- --:--:-- --:--:-- 10923
{
"list": {
"pagination": {
"count": 1,
"hasMoreItems": false,
"totalItems": 1,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"tag": "project-x"
}
}
]
}
}
That’s better if you just wanted a list of tags.
Delete a tag
The last thing you would most likely want to do with tags is to delete them. We can do that with the following DELETE call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/tags/{tagId}
The Node Identifier for the folder or file node to have its tag deleted is specified with the {id}
parameter. And the
tag we want to delete is specified with the {tagId}
parameter.
As an example we will delete the tag that we just added to the file, which has the a427120f-a679-43e6-bcc5-4b0be1f91ea3 Identifier:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/7279b5c5-da55-4e98-8b12-72d33b90c810/tags/a427120f-a679-43e6-bcc5-4b0be1f91ea3' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-* 0
Searching for a tag
Now when you have tagged a lot of files and folders you probably want to find them based on these tags. This can be
done via the /search
API and the TAG:{tag}
keyword.
See the complex search page for an example.
Copy folders and files
Copying folders and files means copying nodes.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/copyNode
Copying folders and files is a useful feature when you want to base new work on something that already exists. The following HTTP POST call is used:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/copy
The Node Identifier for the folder or file node to be copied is specified with the {id}
parameter. The destination
folder is specified with the following POST request data:
{
"targetParentId": "<a folder node identifier>"
}
If a folder is copied, then all its content (i.e. sub folders and files) is also copied recursively. The copied folder
or file will have the same name in the new location, unless you pass on a name
property with a new name.
The following call will copy a text file identified with the 3817c238-976f-426a-9a18-c6727dbab9dd Node Identifier to a destination folder identified with the Node Identifier e5989b9a-3fad-4742-8b14-bef1e13efd25:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/3817c238-976f-426a-9a18-c6727dbab9dd/copy' -d '{ "targetParentId": "e5989b9a-3fad-4742-8b14-bef1e13efd25" }' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 669 0 609 100 60 4578 451 --:--:-- --:--:-- --:--:-- 5030
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-30T12:42:03.055+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 19,
"encoding": "UTF-8"
},
"parentId": "e5989b9a-3fad-4742-8b14-bef1e13efd25",
"aspectNames": [
"cm:copiedfrom",
"cm:titled",
"cm:auditable",
"app:inlineeditable"
],
"createdAt": "2019-09-30T12:42:03.055+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "ae84a06e-1b70-404a-94ed-20faf8ebaf5e",
"properties": {
"app:editInline": true
}
}
}
Move folders and files
Moving folders and files means moving nodes.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/moveNode
Moving folders and files is a useful feature when you want to restructure your repository. The following HTTP POST call is used:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/move
The Node Identifier for the folder or file node to be moved is specified with the {id}
parameter. The destination
folder is specified with the following POST request data:
{
"targetParentId": "<a folder node identifier>"
}
If a folder is moved, then all its content (i.e. sub folders and files) is also moved recursively. The moved folder or file will have the same name at the destination, unless you pass on a name property with a new name.
The following call will move a text file identified with the 3817c238-976f-426a-9a18-c6727dbab9dd Node Identifier to a destination folder identified with the node identifier e5989b9a-3fad-4742-8b14-bef1e13efd25:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/3817c238-976f-426a-9a18-c6727dbab9dd/move' -d '{ "targetParentId": "e5989b9a-3fad-4742-8b14-bef1e13efd25" }' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 773 0 713 100 60 3961 333 --:--:-- --:--:-- --:--:-- 4294
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-30T12:59:32.725+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 19,
"encoding": "UTF-8"
},
"parentId": "e5989b9a-3fad-4742-8b14-bef1e13efd25",
"aspectNames": [
"rn:renditioned",
"cm:titled",
"app:inlineeditable",
"cm:auditable",
"cm:thumbnailModification"
],
"createdAt": "2019-09-30T12:35:29.239+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "3817c238-976f-426a-9a18-c6727dbab9dd",
"properties": {
"app:editInline": true,
"cm:lastThumbnailModification": [
"pdf:1569846933467",
"doclib:1569847995534"
]
}
}
}
Lock a file for editing
Locking a file is sometimes necessary when you want to edit it while no one else should be able to.
API Explorer URL:
- http://localhost:8080/api-explorer/#!/nodes/lockNode
- http://localhost:8080/api-explorer/#!/nodes/unlockNode
Now, what if you wanted to make some changes to a file and not let anyone else make changes until you’ve finished?
For this situation we can lock the file by POSTing an empty JSON object (i.e. {}
) to:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/lock
The Node Identifier for the file to be locked is specified with the {id}
parameter.
The following call will lock a text file identified with the 90d0dd09-93d2-448c-9c23-24de24c3f6ff Node Identifier:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/90d0dd09-93d2-448c-9c23-24de24c3f6ff/lock' -d '{}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 819 0 817 100 2 1695 4 --:--:-- --:--:-- --:--:-- 1695
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-07T14:01:29.943+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 20,
"encoding": "UTF-8"
},
"parentId": "3e59f24a-3a5b-4370-b98e-10e5514ac24e",
"aspectNames": [
"cm:versionable",
"cm:titled",
"cm:lockable",
"app:inlineeditable",
"cm:auditable",
"cm:taggable",
"cm:author"
],
"createdAt": "2019-10-03T08:47:12.852+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "90d0dd09-93d2-448c-9c23-24de24c3f6ff",
"properties": {
"cm:lockType": "WRITE_LOCK",
"cm:lockOwner": {
"id": "admin",
"displayName": "Administrator"
},
"cm:versionType": "MINOR",
"cm:versionLabel": "1.4",
"cm:lockLifetime": "PERSISTENT",
"app:editInline": true
}
}
}
The lock call results in the response above where we can see that the node has been locked with a WRITE_LOCK
. The lock
is owned by the admin user. We can also see that the cm:lockable
aspect has been applied. Note that you can only
lock files, more specifically anything of the type cm:content
and its subtypes.
When you retrieve a file or a listing of files it is also possible to include an isLocked
property so you don’t have
to parse the lock associated aspect and properties.
As the owner of the lock you can make changes to the file including the content, using the update content URL to update the content and generate a new version. However, if you try the same request as another user you’ll get a 409 Conflict error response.
To unlock the file once you’re done with your change you can POST an empty JSON object to:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/unlock
The Node Identifier for the file to be unlocked is specified with the {id}
parameter.
The following call will unlock a text file identified with the 90d0dd09-93d2-448c-9c23-24de24c3f6ff Node Identifier:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/90d0dd09-93d2-448c-9c23-24de24c3f6ff/unlock' -d '{}' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 687 0 685 100 2 6116 17 --:--:-- --:--:-- --:--:-- 6133
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-10-07T14:01:29.943+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 20,
"encoding": "UTF-8"
},
"parentId": "3e59f24a-3a5b-4370-b98e-10e5514ac24e",
"aspectNames": [
"cm:versionable",
"cm:titled",
"app:inlineeditable",
"cm:auditable",
"cm:taggable",
"cm:author"
],
"createdAt": "2019-10-03T08:47:12.852+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"id": "90d0dd09-93d2-448c-9c23-24de24c3f6ff",
"properties": {
"cm:versionType": "MINOR",
"cm:versionLabel": "1.4",
"app:editInline": true
}
}
}
We can now see that the cm:lockable
aspect is no longer applied and the associated properties have been removed.
Create a link to a file
Create a link to a file or folder stored somewhere else.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/createNode
See also: How to manage associations between folders and files
Sometimes it’s really useful to be able to create a link in a folder to a file or folder stored somewhere else. You want one source of truth for your content and avoid uploading a file more than once.
It’s possible to link to both folders and files. The content model type for a file link is app:filelink
and it’s
defined as follows:
<type name="app:filelink">
<title>File Link Object</title>
<parent>cm:link</parent>
</type>
It inherits from the content model type cm:link
, which is also the base type for the folder link type app:folderlink
.
The cm:link
type is defined as follows:
<type name="cm:link">
<title>Link Object</title>
<parent>cm:cmobject</parent>
<properties>
<property name="cm:destination">
<title>Link Destination</title>
<type>d:noderef</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>
By the looks of it we would need to make a POST call with what link type we want, the destination node, and a name of the link:
{
"name": "The name of this link",
"nodeType": "[app:filelink | app:folderlink]",
"properties" : {
"cm:destination" : "file or folder Node Identifier"
}
}
The following HTTP POST call is used (same as when creating file and folder nodes):
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/children
So we are basically making a POST call to the children collection for a folder where we want to create a link.
The {id}
parameter represents the folder under which we want to create the link. The id
can be either one of the
constants -root-
, -shared-
, -my-
or an Alfresco Node Identifier (e.g. d8f561cc-e208-4c63-a316-1ea3d3a4e10e).
As an example we will look at how to create a file link under a folder called My Folder with Node Identifier 8eadf31b-46e9-45cc-81d0-b6d2f2141d22.
We will POST the following data where the link is to a text file identified by the Node Identifier 7279b5c5-da55-4e98-8b12-72d33b90c810:
{
"name": "Link to a text file",
"nodeType": "app:filelink",
"properties" : {
"cm:destination" : "7279b5c5-da55-4e98-8b12-72d33b90c810"
}
}
The call looks like this:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUX2I1YmVjODNkZTQ2ZDI5NDAzMTMzZTk2N2EwYjNjYmE5NjExYmYzOWY=' -d '{ "name": "Link to a text file", "nodeType": "app:filelink", "properties" : { "cm:destination" : "7279b5c5-da55-4e98-8b12-72d33b90c810" } }' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/8eadf31b-46e9-45cc-81d0-b6d2f2141d22/children' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 642 0 503 100 139 7289 2014 --:--:-- --:--:-- --:--:-- 9171
{
"entry": {
"aspectNames": [
"cm:auditable"
],
"createdAt": "2019-12-05T07:43:19.536+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-12-05T07:43:19.536+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "Link to a text file",
"id": "56671a45-48a7-4640-9fb5-04887a8eb7d2",
"nodeType": "app:filelink",
"properties": {
"cm:destination": "7279b5c5-da55-4e98-8b12-72d33b90c810"
},
"parentId": "8eadf31b-46e9-45cc-81d0-b6d2f2141d22"
}
}
We can see that the file link was created successfully with the Node Identifier (id
) returned in the response.
Delete a folder or file
Deleting a node, such as a folder or file, is easy. Here is how to do it.
API Explorer URL: http://localhost:8080/api-explorer/#!/nodes/deleteNode
See also: How to restore a deleted file
There comes a point when you want to remove a folder or file from the Repository. This can be done with the following HTTP DELETE call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}
The Node Identifier for the folder or file node to be deleted is specified with the {id}
parameter.
The following call will delete the text file identified with the d8f561cc-e208-4c63-a316-1ea3d3a4e10e Node Identifier:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-* 0
This doesn’t actually delete the node permanently, but instead it ends up in what’s referred to as the Trashcan. So it’s
only soft deleted and can be restored if needed. If you want the file to be permanently deleted, then you can append a
parameter called permanent
and set it to true
. This only works if you are the owner of the file or an administrator.
Delete file rendition
Note: this endpoint is available in Content Services 7.1.1 and newer versions.
Deleting a file node rendition is easy. Here is how to do it.
API Explorer URL: http://localhost:8080/api-explorer/#!/renditions/deleteRendition
See also:
To remove a file rendition, such as doclib
, pdf
, avatar
, imgpreview
, medium
etc use the following
HTTP DELETE call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/renditions/{renditionId}
The Node Identifier for the file node whose rendition is to be deleted is specified with the {id}
parameter. The
rendition identifier is specified with the {renditionId}
parameter.
The following call will delete a thumbnail rendition with id doclib
for the text file identified with the
d8f561cc-e208-4c63-a316-1ea3d3a4e10e
Node Identifier:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/renditions/doclib'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-* 0
If the rendition is successfully deleted then the content for that rendition node will be cleared.
Delete file version rendition
Note: this endpoint is available in Content Services 7.1.1 and newer versions.
Deleting a rendition for a specific version of a file node is easy. Here is how to do it.
API Explorer URL: http://localhost:8080/api-explorer/#!/versions/deleteVersionRendition
See also:
To remove a rendition, such as doclib
, pdf
, avatar
, imgpreview
, medium
etc for a specific version of a file,
use the following HTTP DELETE call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/{id}/versions/{versionId}/renditions/{renditionId}
The Node Identifier for the file node whose rendition is to be deleted is specified with the {id}
parameter. The
file version is specified with the {versionId}
parameter, which is the version label. The rendition identifier is
specified with the {renditionId}
parameter.
The following call will delete a rendition with id doclib
for the text file version identified with the
d8f561cc-e208-4c63-a316-1ea3d3a4e10e
Node Identifier and the 2.0
version label:
$ curl -X DELETE -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/d8f561cc-e208-4c63-a316-1ea3d3a4e10e/versions/2.0/renditions/doclib/'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-* 0
If the rendition is successfully deleted then the content for that rendition node will be cleared.
List deleted folders and files (Trashcan)
Listing the content of the so called trashcan is useful if you want to restore soft deleted nodes.
API Explorer URL: http://localhost:8080/api-explorer/#!/trashcan/listDeletedNodes
See also:
When folders and files are deleted from the Repository, they are actually not physically deleted. They just have their metadata changed a bit so they don’t show up in the user interface. They live in what’s referred to as the Trashcan. If you want to list stuff contained in the Trashcan, then you use the following GET call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes
This will list nodes, such as folders and files, that the currently logged in user is the owner of. If you are logged in as an Administrator, then you will see all deleted nodes.
Here is how to make this call:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5737 100 5737 0 0 101k 0 --:--:-- --:--:-- --:--:-- 101k
{
"list": {
"pagination": {
"count": 10,
"hasMoreItems": false,
"totalItems": 10,
"skipCount": 0,
"maxItems": 100
},
"entries": [
{
"entry": {
"createdAt": "2019-09-09T07:38:55.060+0000",
"archivedAt": "2019-09-09T10:18:38.832+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-09T10:05:50.020+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "newfilename2.txt",
"archivedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"id": "d8f561cc-e208-4c63-a316-1ea3d3a4e10e",
"nodeType": "acme:document",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 40,
"encoding": "ISO-8859-1"
}
}
},
{
"entry": {
"createdAt": "2019-09-05T08:52:16.785+0000",
"archivedAt": "2019-09-05T08:58:20.746+0000",
"isFolder": false,
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2019-09-05T08:52:16.785+0000",
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "somefile.txt",
"archivedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"id": "8f1c3f76-0eaf-452a-be66-c5405af67dbc",
"nodeType": "cm:content",
"content": {
"mimeType": "text/plain",
"mimeTypeName": "Plain Text",
"sizeInBytes": 34,
"encoding": "ISO-8859-1"
}
}
},
. . .
The response starts with the familiar pagination
object, which tells you how many entries
, in this case deleted nodes,
that was returned with this call.
Restore deleted folders and files (Trashcan)
Folders and files are “soft deleted”, meaning they are not physically gone from the system when deleted, so they can be restored as described on this page.
API Explorer URL: http://localhost:8080/api-explorer/#!/trashcan/restoreDeletedNode
See also:
Soft deleted folders and files that live in the so called Trashcan can be restored so they are visible in the user interface again. They will show up in the folder from where they were deleted. Use the following POST call:
http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/{id}/restore
The {id}
parameter represents the node identifier and you can get it by first listing the deleted nodes.
Let’s say that we have the following deleted image file node called luna_1.jpeg with Node ID 4bda2a4d-5013-46fa-93bd-104dd58e1596
,
we can get info about it with the following /deleted-nodes/{node id}
GET call:
$ curl -X GET -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/4bda2a4d-5013-46fa-93bd-104dd58e1596?include=path' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1220 0 1220 0 0 6354 0 --:--:-- --:--:-- --:--:-- 6354
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2020-03-02T07:34:07.547+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "image/jpeg",
"mimeTypeName": "JPEG Image",
"sizeInBytes": 5704,
"encoding": "UTF-8"
},
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:thumbnailModification",
"exif:exif",
"cm:ownable",
"cm:titled",
"cm:dublincore",
"cm:auditable",
"cm:author"
],
"createdAt": "2020-02-25T14:05:21.137+0000",
"archivedAt": "2020-03-02T07:36:22.001+0000",
"path": {
"name": "/Company Home",
"isComplete": true,
"elements": [
{
"id": "ffbc5c54-2efb-4b84-8ba9-40450620023c",
"name": "Company Home",
"nodeType": "cm:folder",
"aspectNames": [
"cm:titled",
"cm:auditable",
"app:uifacets"
]
}
]
},
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "luna_1.jpeg",
"archivedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"id": "4bda2a4d-5013-46fa-93bd-104dd58e1596",
"properties": {
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:subject": "adds the aspect automatically",
"exif:pixelYDimension": 225,
"cm:owner": {
"id": "admin",
"displayName": "Administrator"
},
"exif:pixelXDimension": 225,
"cm:lastThumbnailModification": [
"doclib:1582639531285",
"imgpreview:1582639533828"
]
}
}
}
Note that by setting the include
parameter to path
the full folder path for the deleted node is also returned. In
this case the node was stored under /Company Home.
To restore this node we can make the following /deleted-nodes/{node id}/restore
POST call:
curl -X POST -H 'Content-Type: text/plain;charset=UTF-8' -H 'Content-Length: 0' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' 'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/4bda2a4d-5013-46fa-93bd-104dd58e1596/restore' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 883 0 883 0 0 2201 0 --:--:-- --:--:-- --:--:-- 2196
{
"entry": {
"isFile": true,
"createdByUser": {
"id": "admin",
"displayName": "Administrator"
},
"modifiedAt": "2020-03-02T07:34:07.547+0000",
"nodeType": "cm:content",
"content": {
"mimeType": "image/jpeg",
"mimeTypeName": "JPEG Image",
"sizeInBytes": 5704,
"encoding": "UTF-8"
},
"parentId": "ffbc5c54-2efb-4b84-8ba9-40450620023c",
"aspectNames": [
"rn:renditioned",
"cm:versionable",
"cm:titled",
"cm:dublincore",
"cm:auditable",
"cm:author",
"cm:thumbnailModification",
"exif:exif"
],
"createdAt": "2020-02-25T14:05:21.137+0000",
"isFolder": false,
"modifiedByUser": {
"id": "admin",
"displayName": "Administrator"
},
"name": "luna_1.jpeg",
"id": "4bda2a4d-5013-46fa-93bd-104dd58e1596",
"properties": {
"exif:pixelYDimension": 225,
"cm:versionType": "MAJOR",
"cm:versionLabel": "1.0",
"cm:subject": "adds the aspect automatically",
"exif:pixelXDimension": 225,
"cm:lastThumbnailModification": [
"doclib:1582639531285",
"imgpreview:1582639533828"
]
}
}
}
The response contains an entry
object with the metadata for the restored node.
Note that when you make the above restore POST call there’s no JSON data supplied. Because of this it’s important that
the -H 'Content-Type: text/plain;charset=UTF-8' -H 'Content-Length: 0'
headers are set.
Now, if the target folder is no longer there (i.e. it’s been deleted after the file node was deleted), then you must
supply a new parent folder node id (targetParentId
) with a call looking like this:
$ curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Authorization: Basic VElDS0VUXzA4ZWI3ZTJlMmMxNzk2NGNhNTFmMGYzMzE4NmNjMmZjOWQ1NmQ1OTM=' -d '{
"targetParentId": "5a858591-752f-49d0-b686-e2e1a830ea8d",
"assocType": "cm:contains"
}'
'http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/deleted-nodes/4bda2a4d-5013-46fa-93bd-104dd58e1596/restore' | jq
...