REST API

TL;DR

Run this to create or replace a page under /content/demo/shared/import-demo/pages/example-1 and make it live at https://www.buffalo.edu/content/demo/shared/import-demo/pages/example-1.html:

# create (or update) a page
$ curl https://ubcms-author.buffalo.edu/content/demo/shared/import-demo/pages \
  -u username:password \
  -H accept:application/json \
  -F :operation=import \
  -F :contentType=json \
  -F :replace=true \
  -F :name=example-1 \
  -F :content='
  {
    "jcr:primaryType": "cq:Page",
    "jcr:content": {
      "jcr:primaryType": "cq:PageContent",
      "jcr:title": "Example 1",
      "sling:resourceType": "sharedcontent/components/page/unstructuredpage",
      "par": {
        "sling:resourceType": "foundation/components/parsys",
        "title": {
          "sling:resourceType": "wci/components/content/title"
        },
        "text": {
          "sling:resourceType": "wci/components/content/text",
          "text": "<p>Hello World!</p>"
        }
      }
    }
  }'
  
{
  "path": "/content/demo/shared/import-demo/pages/page1",
  "status.message": "OK",
  "status.code": 200,
  ...
}
# activate the page
$ curl https://ubcms-author.buffalo.edu/bin/replicate.json \
  -u username:password \
  -F cmd=Activate \
  -F path=/content/demo/shared/import-demo/pages/example-1

Manipulating content (REST API)

Manipulating content via REST API is handled by the Sling POST Servlet, which is documented in detail here:
https://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-post.html

This is actually used extensively by the UBCMS/AEM user interface, too, so you can observe calls to it in the browser dev tools network panel when you save a dialog box, and other times.

We will mostly use what's described under Importing Content Structures.

Authentication

You will need to authenticate for all of these examples. For all UBCMS requests, you can pass basic authentication headers with any UBCMS username and password (e.g. "-u" in curl).

For testing and development you can use your personal account. For production, it should probably  be a dedicated admin/script account. You can use any admin account with a full UBITname account you already have -- we can just add that to any group that needs access. We can also create a user just within UBCMS if that seems best. We shiould probably also create dedicated groups that only have access to what the script needs access to.

Test/development environments

You can replace any of the ubcms-author.buffalo.edu URLs here with ubcms-qa.buffalo.edu for a safe test environment. Content and user accounts on QA are copied weekly from production. Other test and development environments are available for various uses, too.

Data model

You generally don't have to totally understand the data model (the full json structure). You can just edit the template page and see what comes out.

You can view a page's data model in JSON format by modifying the URL. You will need to remove "/cf#" (Classic UI) or "/editor.html" (Flex UI) from the beginning of the path, and change ".html" to ".5.json" at the end. "5" is the number of levels deep to display. You can use ".infity.json", but that's pretty dangerous if there are lots of sub-pages. JSON output is mostly blocked on live pages so you'll need to use ubcms-author URLs for this.

For example, you might edit the Template page at:
https://ubcms-author.buffalo.edu/editor.html/content/demo/shared/import-demo/template.html

You can view its JSON data at:
https://ubcms-author.buffalo.edu/content/demo/shared/import-demo/template.5.json

You can also view and edit this data in the CRXDE tool. You can jump right there at this URL:
https://ubcms-author.buffalo.edu/crx/de/index.jsp#/content/demo/shared/import-demo/template

You can retrieve this JSON programmatically with curl, too. You will need to use basic authentication credentials:

$ curl -u username:password \
  https://ubcms-author.buffalo.edu/content/demo/shared/import-demo/template.5.json

{
  "jcr:primaryType": "cq:Page",
  ...
}

Simplified page example

Lots of stuff in the data model is not necessary and can be left out to simplify the data generation.

Data model details

One very important detail to understand about the data structure is that every page both has its own content, and is also potentially a parent of child pages. This is separated in the data model. The content of a page (like its title, metadata, and what's actually displayed on the page) is always under its "jcr:content" node. Any child pages are direct descendents (so, siblings of the jcr:content node).

Important properties in the data:

  • jcr:primaryType is one of the most important properties. It is important to the data structure. This data will probably always use cq:Page, cq:PageContent (for jcr:content nodes) and nt:unstructured (basically can contain any properties and child nodes). jcr:primaryType can be left off to default to nt:unstructured.
  • sling:resourceType is another important property. It generally corresponds to how that node is rendered. It will not be present on cq:Page nodes. It will correspond to the type of page (template) for cq:PageContent nodes. It will correspond to the component for other nodes.
  • Some properties are the names of child nodes. These names may need to have a specific name, like "jcr:content" or "par". They may have a meaningful name, like the name of a page. They may have an arbitrary name, like the name of a component node under "par", like "text" or "title_1".
  • Properties on nodes with a sling:resourceType property typically correspond to settings from the page or component dialog box.
  • The order of properties from dialog boxes does not matter. The order of child pages and child nodes (components) do.

Putting it all together

The following curl command will create a new, simple page at /content/demo/shared/import-demo/pages/example-1. You will need to fill in a working username/password with access to create pages in that area.

# create (or update) a page
$ curl https://ubcms-author.buffalo.edu/content/demo/shared/import-demo/pages \
  -u username:password \
  -H accept:application/json \
  -F :operation=import \
  -F :contentType=json \
  -F :replace=true \
  -F :name=example-1 \
  -F :content='
  {
    "jcr:primaryType": "cq:Page",
    "jcr:content": {
      "jcr:primaryType": "cq:PageContent",
      "jcr:title": "Example 1",
      "sling:resourceType": "sharedcontent/components/page/unstructuredpage",
      "par": {
        "sling:resourceType": "foundation/components/parsys",
        "title": {
          "sling:resourceType": "wci/components/content/title"
        },
        "text": {
          "sling:resourceType": "wci/components/content/text",
          "text": "<p>Hello World!</p>"
        }
      }
    }
  }'
  
{
  "path": "/content/demo/shared/import-demo/pages/page1",
  "status.message": "OK",
  "status.code": 200,
  ...
}

After creating this page, you can edit it at https://ubcms-author.buffalo.edu/editor.html/content/demo/shared/import-demo/pages/example-1.html. You will also be able to see it in the list of pages, and view/edit it in CRXDE.

The following curl command will create a page at /content/demo/shared/import-demo/pages/example-2 that follows the example page as a template. A lot of unnecessary properties have been trimmed out, like page owner (will be assumed to be the username used for curl), modification timestamps (will be the current time), and other default values.

$ curl https://ubcms-author.buffalo.edu/content/demo/shared/import-demo/pages \
  -u username:password \
  -H accept:application/json \
  -F :operation=import \
  -F :contentType=json \
  -F :replace=true \
  -F :name=example-2 \
  -F :content='
  {
    "jcr:primaryType": "cq:Page",
    "jcr:content": {
      "jcr:primaryType": "cq:PageContent",
      "jcr:title": "Example 2",
      "cq:tags": [
        "audience:public"
      ],
      "sling:resourceType": "sharedcontent/components/page/unstructuredpage",
      "par": {
        "title": {
          "sling:resourceType": "wci/components/content/title"
        },
        "text": {
          "sling:resourceType": "wci/components/content/text",
          "text": "<p>This is body text HTML.</p>"
        },
        "callout": {
          "sling:resourceType": "wci/components/content/callout",
          "jcr:title": "Callout title (plain text)",
          "text": "<p>Callout body (html)</p>",
          "version": "2",
          "color2": "gray-harriman-blue",
          "icon2": "icon-info"
        },
        "title_2": {
          "sling:resourceType": "wci/components/content/title",
          "jcr:title": "Title H2",
          "type": "h2"
        },
        "image": {
          "sling:resourceType": "wci/components/content/image",
          "fileReference": "/content/dam/demo/dkl_3467.jpg",
          "alt": "building"
        },
        "table": {
          "sling:resourceType": "wci/components/content/table",
          "tableData": "<table width=\"100%\" cellspacing=\"0\"><tbody><tr><th>Col 1</th><th>Col 2</th></tr><tr><td>1</td><td>2</td></tr></tbody></table>"
        }
      }
    }
  }'

Making it live

All of these examples have imported content to the author environment. Changes should be visible in that environment immediately, but none of this is going to the live servers.

There is also an API to publish a page:

$ curl https://ubcms-author.buffalo.edu/bin/replicate.json \
  -u username:password \
  -F cmd=Activate \
  -F path=/content/demo/shared/import-demo/pages/example-1

More notes

  • It's not necessary to trim out default/unnecessary properties and nodes, but it is probably a good thing to keep things minimal to help the template and code be easier to follow. Trial and error is probably the best way to find out what is important and not. Generally if a property is the same as its default dialog box value it can be left off.
  • The "image" and "social" nodes directly under jcr:content correspond to the preview image and social image in the page properties dialog box and can be left off (unless you want to use them).
  • A title component without a jcr:title property will display the page title (same as leaving it blank in the dialog box).
  • Many components accept HTML, but it is almost always filtered — only specific elements and attributes are allowed. If you want to put arbitrary HTML into the page you can add an HTML snippet component to the template.
  • For images (and file downloads) there are ways to use the API to upload files (I can demo), but it might be easiest to just collect the images in one directory structure, upload that manually with WebDAV (or drag-and-drop into the DAM), and then just use the fileReference property as shown here.
  • You should probably plan out not just the format of the page, but the organization and metadata of the page and how it will be used (e.g. referenced by other authors). You may want specific tags on pages. You may want multiple versions (like a full and teaser version) of each piece of content. If the content is all programmatically generated (imported), it should not be hard to import it all, change your mind, tweak the script, delete everything, and re-import it.
  • There are APIs for anything you can do by hand in the CMS, so if you have any questions about how to delete a page, just patch in an update to a small section of a page, move or re-order pages or components, list/query existing content or anything else, ask for assitance at ubcms.buffalo.edu or on our UBCMS developers mailing list.