CRUD Example documentation for the dotCMS Content Management System

This document shows a round trip example of how content can be created and managed via API.

  1. Get an API Token
  2. Create a new content type via api
  3. Add and Publish content
  4. Add draft content with a binary attachment
  5. Edit the content
  6. Push the content through a workflow
  7. Delete the content
  8. Delete the content type
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:8080/api/v1/authentication/api-token \ | python -c 'import json,sys;print json.load(sys.stdin)["entity"]["token"]'` ```

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:8080/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:8080/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:8080/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, retreive 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:8080/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:8080/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:8080/api/v1/workflow/actions/fire?identifier=$conId -H "Content-Type: application/json" -d '{
   "actionName": "Publish",
   "comments": "publishing content"
}'

Retreving Content

Content can be retreived 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:8080/api/content/id/$conId

by query

curl -s -H "Authorization:Bearer $TOK" http://localhost:8080/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:8080/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:8080/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:8080/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:8080/api/v1/contenttype/id/myBlog