Edit this topic

Writing a content part

A content part is a reusable piece of content that can be composed with other parts into a content type. For example, a blog post content type could be built by composing a title part, a text part for the post's body, a tags part, and a list of comments.

DecentCMS comes with a few parts built-in, such as title and text, but it's easy to write your own.

Creating your own content type can be done by adding it to the configuration of the "content" feature in the site's settings.json file. You may also alter an existing content type and add new parts to it.

In this topic, we'll use the example of a rudimentary tags part. The part will expose a list of tags, which are simple strings.

Here's a definition of the page content type, modified to include the new part:

"page": {
  "name": "Page",
  "description": "A simple page type.",
  "parts": {
    "title": {
      "type": "title"
    },
    "body": {
      "type": "text"
    },
    "tags": {
      "type": "tags"
    }
  }
}

In the document for a content item that has this part, it will be stored as follows:

"tags": ["CMS", "DecentCMS", "Content part"]

If the content item just has this extra property, and no code does anything with it, nothing will change. The first bit of code we have to write is a shape handler service. When the content item gets added to the page, it's in the form of a "content" shape. DecentCMS will then make a call to all shape-handler services, that can then decide to handle properties of the content item and add its own shapes under the "content" shape. Shape handlers can decide to act on any shape, but most handlers act only on a specific type of part. There is an easier way to build this type of handler, that eliminates most of the boilerplate code that would otherwise be necessary. Instead of implementing a shape-handler service, we're going to implement a tags-part-handler. The following tags-part.js file should be placed under the services directory of your module.

'use strict';

/**
 * The tags part handler creates tags shapes from tags parts.
 */
var TagsPart = {
  feature: 'sample-tags',
  service: 'tags-part-handler',
  /**
   * Adds a tags shape to `context.shapes` for the tags part on the context.
   * @param {object} context The context object.
   * @param {object} context.part The tags part to handle.
   * @param {string} context.partName The name of the part.
   * @param {string} context.displayType The display type.
   * @param {object} context.item A reference to the content item.
   * @param {Array} context.shapes The shapes array to which new shapes must be pushed.
   * @param {object} context.scope The scope.
   * @param {Function} done The callback.
   */
  handle: function handleTagsPart(context, done) {
    var shapes = context.shapes;
    // No sense in doing anything if there isn't a shapes collection to output to.
    if (!shapes) {done();return;}
    // Push a new shape for this part.
    var part = context.part;
    shapes.push({
      meta: {
        type: 'tags',
        name: context.partName,
        alternates: ['tags-' + context.partName],
        item: context.item
      },
      temp: {displayType: context.displayType},
      tags: part || []
    });
    done();
  }
};

module.exports = TagsPart;

The code first checks that there is a collection of shapes to add new ones to. If there isn't, it just calls done. Otherwise, it just pushes a new shape that has the part itself (which is just an array of strings) as its tags property. We're almost done: the handler created shapes for each tags part, now we need to place these parts, and render them.

Default placement for the parts shape can be defined from the placement.json file at the root of your module:

"tags": {"path": "main", "order": "after"}

This places the tags shape last in the main zone.

Finally, we need a default template for the part. We're going to use the JavaScript view engine for this. The following views/tags.js template will render the list of tags as a ul/li structure:

'use strict';

module.exports = function tagsTemplate(tagsPart, renderer, done) {
  renderer.startTag('ul');
  tagsPart.tags.forEach(function(tag) {
    renderer.tag('li', tag);
  }
  renderer.endTag();
  done();
};