This document shows a round trip example of how content can be created and managed via API.
Step 1: Get an API Token
First we get an API token and store that in an environmental variable:
export TOK=`curl -H "Content-Type:application/json" -s -X POST -d '
{
"user":"admin@dotcms.com",
"password":"admin",
"expirationDays": 1,
"label":"for testing"
}
' http://localhost:8082/api/v1/authentication/api-token \
| python -c 'import json,sys;print json.load(sys.stdin)["entity"]["token"]'`
Step 2: Creating a new content type
Create a new content type by specifiying the type information, the fields and the associated workflows. Here are some notes of interest
Muliti column editing form: You can specify how you want your content to be displayed in the back end by including column and row field information. Adding a column in after a field breaks everything after it into a new column - until a new row is added.
Specify one or more workflows: This content type uses the system workflow, id:
d61a59e1-a49c-46f2-a929-db2b4bfa88b2
UrlMap/Slug field: The content includes a custom field that calls a .vtl included in the dotcms starter site
url-title.vtl
. This generates a slug/urlmap based on the content title. This field has been marked unique.
curl -H "Authorization:Bearer $TOK" -XPOST http://localhost:8082/api/v1/contenttype \
-H "Content-Type: application/json" -d'
{
"clazz": "com.dotcms.contenttype.model.type.SimpleContentType",
"name": "My Blog",
"urlMapPattern": "/blogs/{urlTitle}",
"variable": "myBlog",
"fields": [
{
"clazz": "com.dotcms.contenttype.model.field.RowField",
"name": "fields-0"
},
{
"clazz": "com.dotcms.contenttype.model.field.ColumnField",
"name": "column-0"
},
{
"clazz": "com.dotcms.contenttype.model.field.HostFolderField",
"indexed": true,
"listed": false,
"name": "Site",
"variable": "siteOrFolder"
},
{
"clazz": "com.dotcms.contenttype.model.field.TextField",
"dataType": "TEXT",
"indexed": true,
"listed": true,
"name": "Title",
"regexCheck": "[^(<[.\n]+>)]*",
"required": true,
"searchable": true,
"variable": "title"
},
{
"clazz": "com.dotcms.contenttype.model.field.CustomField",
"dataType": "LONG_TEXT",
"indexed": true,
"unique": true,
"name": "URL Title",
"required": true,
"values": "#dotParse(\"/application/vtl/custom-fields/url-title.vtl\")",
"variable": "urlTitle"
},
{
"clazz": "com.dotcms.contenttype.model.field.TextField",
"dataType": "TEXT",
"indexed": true,
"listed": true,
"name": "Author",
"regexCheck": "[^(<[.\n]+>)]*",
"required": true,
"searchable": true,
"variable": "author"
},
{
"clazz": "com.dotcms.contenttype.model.field.DateTimeField",
"indexed": true,
"listed": true,
"defaultValue": "now",
"name": "Publish Date",
"required": true,
"searchable": true,
"variable": "publishDate"
},
{
"clazz": "com.dotcms.contenttype.model.field.ColumnField",
"name": "column-1"
},
{
"clazz": "com.dotcms.contenttype.model.field.BinaryField",
"name": "Image",
"variable": "image"
},
{
"clazz": "com.dotcms.contenttype.model.field.TagField",
"indexed": true,
"name": "Tags",
"searchable": true,
"variable": "tags"
},
{
"clazz": "com.dotcms.contenttype.model.field.RowField",
"name": "fields-3"
},
{
"clazz": "com.dotcms.contenttype.model.field.ColumnField",
"name": "column-4"
},
{
"clazz": "com.dotcms.contenttype.model.field.WysiwygField",
"indexed": true,
"name": "Body",
"required": true,
"searchable": true,
"variable": "body"
}
],
"workflow": ["d61a59e1-a49c-46f2-a929-db2b4bfa88b2"]
}'
Step 3: Adding Content with Workflow
In these examples we will use the /api/v1/workflow/actions/fire
content endpoint that allows us to specify what workflow action we want dotCMS to execute on the content when we PUT it to dotCMS. You can specify the workflow action either by id or by Name. In this case we want to “Save / Publish” the content which will automatically publish the new piece of content. The comments property is optional and adds a comment to the content's workflow.
This creates a new content object of our new content type, specified by the "contentType":"myBlog"
value. The site or folder specifies where the content will live in dotCMS. Valid values include the id of the site or folder, the site name or the site name + the path to the folder. Not that in the body field, we can include HTML if appropiate.
curl -v -H "Authorization:Bearer $TOK" -XPUT http://localhost:8082/api/v1/workflow/actions/fire \
-H "Content-Type: application/json" -d '
{
"actionName": "Save / Publish",
"comments": "saving content",
"contentlet": {
"contentType":"myBlog",
"title":"First Content",
"urlTitle": "my-url-title",
"siteOrFolder":"demo.dotcms.com:/about-us",
"publishDate":"2019-12-18 10:00:00",
"author": "Mr. Admin",
"tags": "tag one, tag two",
"body": "<h1>This is a content in amazing</h1><div>but not this</div>",
"languageId": "1"
}
}'
Step 4: Full CRUD with Content and Workflows
Create an unpublished content object, publish it, retrieve it, archive it and finally delete it.
Save a draft content object with a file
In this example, we save a content object with a image file found on our local machine at /tmp/test.jpg
and we store the returned content identifier
in an environmental variable. We specify the workflow “save” action, e.g. "actionName": "save",
. This does not automatically publish our content and instead leaves it in a drafted state. note: When sending a file, curl
defaults to use the Content-Type: multipart/form-data
.
Note: the first step sets the environmental variable
$conId
to the identifier of our newly created content. The following examples rely on that $conId to be set, but could be replaced with the content identifier manually.
export conId=$(curl -s -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire \
-F 'json={
"actionName": "save",
"comments": "saving content",
"contentlet": {
"contentType":"myBlog",
"title":"Amazing Second Content",
"urlTitle": "my-second-url-title",
"siteOrFolder":"demo.dotcms.com",
"publishDate":"2019-12-18 10:00:00",
"author": "Mr. Admin",
"tags": "tag one, tag two",
"body": "<h1>This is a content is amazing</h1><div>but not this</div>",
"languageId": "1"
}
}; type=application/json' -F "file=@/tmp/test.jpg; type=image/jpeg" | python -c 'import json,sys;print json.load(sys.stdin)["entity"]["identifier"]' )
Editing Existing Content
This edits our new content object. dotCMS uses the content identitifer
+ the langaugeId
to know if we are creating a new content object or are editing an existing content object. In this case, we are editing because we have specified the identifier
plus the languageId
of our content.
curl -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire \
-F "json={
'actionName': 'save',
'comments': 'saving content again',
'contentlet': {
'contentType':'myBlog',
'identifier':\"$conId\",
'title':'Edited Amazing Second Content',
'urlTitle': 'my-second-url-title',
'siteOrFolder':'demo.dotcms.com',
'publishDate':'2019-12-18 11:00:00',
'author': 'Mr. Admin Again',
'tags': 'tag three, tag four',
'body': '<h1>This is a content is still amazing</h1><div>but not this</div>',
'languageId': '1'
}
}; type=application/json" -F 'file=@/tmp/test.jpg; type=image/jpeg'
Publishing Content via Workflow
Using the identifier, we can fire the “PUBLISH” workflow action on the saved content. The available actions are permission based, so only users with permissions to execute the actions can fire them.
curl -s -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire?identifier=$conId -H "Content-Type: application/json" -d '{
"actionName": "Publish",
"comments": "publishing content"
}'
Retreving Content
Content can be retrieved either by identifier+langauge (defaults to the default language), by inode (aka version), or can be queried from the content store.
by id
curl -s -H "Authorization:Bearer $TOK" http://localhost:8082/api/content/id/$conId
by query
curl -s -H "Authorization:Bearer $TOK" http://localhost:8082/api/content/query/+contentType:myBlog%20+myBlog.urlTitle:my-second-url-title
Unpublish the content
Using the identifier, we can fire the “UNPUBLISH” workflow action on the saved content. The available actions are permission based, so only users with permissions to execute the actions can fire them.
curl -s -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire?identifier=$conId -H "Content-Type: application/json" -d '{
"actionName": "Unpublish",
"comments": "unpublishing content"
}'
Archive the content
Using the identifier, we can fire the “ARCHIVE” workflow action on the saved content. The available actions are permission based, so only users with permissions to execute the actions can fire them.
curl -s -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire?identifier=$conId -H "Content-Type: application/json" -d '{
"actionName": "Archive",
"comments": "Archiveing content"
}'
Delete the content
Using the identifier, we can fire the “DELETE” workflow action on the saved content. A delete destroys the content object in the specified langauge. If no language is specified, it will delete the content of the default language.
curl -s -H "Authorization:Bearer $TOK" -X PUT http://localhost:8082/api/v1/workflow/actions/fire?identifier=$conId -H "Content-Type: application/json" -d '{
"actionName": "Delete",
"comments": "Deleting content"
}'
Step 5: Delete an existing content type
Delete any existing content type that might already have been created, based on its content type variable. This will also delete any content of this content type if it exists. This uses the token $TOK created in Step 1.
curl -s -H "Authorization:Bearer $TOK" -H "Content-Type: application/json" -XDELETE \
http://localhost:8082/api/v1/contenttype/id/myBlog