Renditionable

QName: f:renditionable

Marks that an instance or type is to have one or more renditions.

Renditions are other nodes in the branch whose content is automatically generated and kept in sync as the source node is created, updated and deleted (or has its relevant attachment modified).

Configuration

<thead>
    <tr>
        <th>Property</th>
        <th>Type</th>
        <th>Default</th>
        <th nowrap>Read-Only</th>
        <th>Description</th>
    </tr>
</thead>
<tbody>
    <tr>
        <td>schedule</td>
        <td>text</td>
        <td></td>
        <td></td>
        <td>
            Determines when renditions will be generated.
            <br/>
            Either <code>synchronous</code> or <code>asynchronous</code>.
        </td>
    </tr>
    <tr>
        <td>renditions</td>
        <td>object</td>
        <td></td>
        <td></td>
        <td>
            Defines the renditions that you would like to have generated.  Each rendition is keyed with a name
            and provides a configuration that is passed to a rendition engine for execution.
        </td>
    </tr>
</tbody>

Renditionable Example (using a scripts)

Here is a node that has the f:renditionable feature enabled. The general form looks something like this:

{
    "title": "My Article",
    "longHeadline": "Cubs win the World Series in game 6 by a score of 201 to 3 in Wrigley Field, Chicago!",
    "mediumHeadline": "Cubs win World Series by score of 201 to 3!";
    "shortHeadline": "Cubs win World Series!";
    "_features": {
        "f:renditionable": {
            "renditions": {
                "{renditionKey}": {
                    "engineId": "{id of the renditioning engine to use}",
                    "targetNodeId": "{targetNodeId}",
                    "targetAttachmentId": "{targetAttachmentId}",
                    "targetAttachmentMimeType": "{targetAttachmentMimeType}",
                }
            }
        }
    }
}

The exact properties that should be specified depend on the requirements of the renditioning engine. Here is a more concrete example that executes a script to create the new node:

{
    "title": "My Article",
    "longHeadline": "Cubs win the World Series in game 6 by a score of 201 to 3 in Wrigley Field, Chicago!",
    "mediumHeadline": "Cubs win World Series by score of 201 to 3!";
    "shortHeadline": "Cubs win World Series!";
    "_features": {
        "f:renditionable": {
            "renditions": {
                "mobile": {
                    "engineId": "script",
                    "scriptNodeId": "custom:script1"
                }
            }
        }
    }
}

The script engine will run the JavaScript contained as the default attachment on the script node. The script node is identified by custom:script1. Note that this the QName for the script node but it may also be identified by it's node _doc property.

For the script engine, a simple rendition method must be provided that takes in a node and returns the JSON for the resulting object.

function rendition(node) {
    return {
        "title": node.data.title,
        "headline": node.data.shortHeadline,
        "format": "mobile"
    };
}

As a result, a new node will be generated with the JSON:

{
    "title": "My Article",
    "headline": ""Cubs win World Series!",
    "format": "mobile"
}

The new node is marked with the f:rendition feature. It has an association of type a:has_rendition that connects it from the source node to the rendition. This association has a property on it called renditionKey with the value mobile.

The source node will be marked with the f:renditioned feature to indicate that a rendition was generated. That way, in the future, if you ever update the source node, all of the target renditions will likewise be regenerated.

For example, you might modify the source node's shortHeadline to be "Cubs lose World Series!". Upon saving the source node, the target rendition (above) will update to:

{
    "title": "My Article",
    "headline": ""Cubs lose World Series!",
    "format": "mobile"
}

Which would be a sad thing indeed. But here in Chicago, we're quite used to the Cubs losing. We'll get over it. And we'll be back next season!

Rendition Engines

The following rendition engines are available:

  • attachment
  • copy
  • nodelist
  • script

Attachment Rendition Engine (attachment)

This rendition engine will copy and optionally transform an attachment from the document source to the target rendition.

Here is an example of an article that has a download rendition configured for it. The rendition takes the article's data attachment and copies it to a renditioned node as that node's default attachment. The renditioned attachment is converted to application/pdf. If the renditioned node doesn't yet exist, it will be created and it will have the Type QName of my:download.

{
    "title": "My Article",
    "_type": "my:article",
    "_features": {
        "f:renditionable": {
            "renditions": {
                "download": {
                    "engineId": "attachment",
                    "sourceAttachmentId": "data",
                    "targetAttachmentId": "default",
                    "targetTypeQName": "my:download",
                    "targetAttachmentMimeType": "application/pdf"
                }
            }
        }
    }
}

As users make changes to the article, the rendition will be kept in sync.

The content graph will look like:

[My Article (my:article)] -> (a:has_rendition) -> [Rendition Node (my:download)]

Copy Rendition Engine (copy)

This rendition engine will copy a source document to a target rendition.

Here is an example of an article that has a copy rendition configured for it. The rendition takes the article's source JSON and creates a copy of the given target type.

{
    "title": "My Article",
    "_type": "my:article",
    "_features": {
        "f:renditionable": {
            "renditions": {
                "copy": {
                    "engineId": "copy"
                }
            }
        }
    }
}

As users make changes to the article, the rendition will be kept in sync.

The content graph will look like:

[My Article (my:article)] -> (a:has_rendition) -> [Rendition Node (my:article)]

NodeList Rendition Engine (nodelist)

This rendition engine will execute a script to map a source document into a target document that is a list item in a node list.

Here is an example of a custom:book content type that has f:renditionable as a mandatory feature. This feature is configured with a books rendition that will map the source document into a new list item content type (custom:booksListItem). The resulting instance will then be added to a data list where key is books.

{
	"_qname":"custom:book",
	"_type":"d:type",

    "type":"object",
	"description":"Node List Test Book Type",
    "properties":{},

    "mandatoryFeatures": {
        "f:renditionable": {
            "renditions": {
                "books": {
                    "engineId": "nodelist",
                    "scriptNodeId": "custom:script1",
                    "listKey": "books",
                    "listItemType": "custom:booksListItem"
                }
            }
        }
    }
}

The custom:booksListItem content type might look like this:

{
	"_qname":"custom:booksListItem",
	"_type":"d:type",
    "type":"object",
	"description":"Books list item type",
    "properties":{
        "title": {
            "type": "string"
        },
        "author": {
            "type": "string"
        }
    }
}

The script (custom:script) that we use to make article instances into book list items might look like this:

function rendition(node)
{
    return {
        "title": node.data.title,
        "author": node.data.author
    };
}

Thus, when users make changes to books, the renditioning engine will produce list item instances by executing the mapping function above against those books. Each book will sync to one row in the resulting list.

The graph ends up looking like this:

Book 1 (custom:book) -> (a:has_rendition) -> Rendition List Item 1 (custom:booksListItem) <- (a:has_item) <----\
Book 2 (custom:book) -> (a:has_rendition) -> Rendition List Item 2 (custom:booksListItem) <- (a:has_item) <--\ \
Book 3 (custom:book) -> (a:has_rendition) -> Rendition List Item 3 (custom:booksListItem) <- (a:has_item) <------ List (books)
Book 4 (custom:book) -> (a:has_rendition) -> Rendition List Item 4 (custom:booksListItem) <- (a:has_item) <--/ /
Book 5 (custom:book) -> (a:has_rendition) -> Rendition List Item 5 (custom:booksListItem) <- (a:has_item) <---/

Script Rendition Engine (script)

This rendition engine will execute a custom server-side script to produce a target JSON that will constitute the renditioned node.

A good example is already shown above. In essence, a source article (shown here) can have a rendition defined on it that runs a script, like this:

{
    "title": "My Article",
    "longHeadline": "Cubs win the World Series in game 6 by a score of 201 to 3 in Wrigley Field, Chicago!",
    "mediumHeadline": "Cubs win World Series by score of 201 to 3!";
    "shortHeadline": "Cubs win World Series!";
    "_features": {
        "f:renditionable": {
            "renditions": {
                "mobile": {
                    "engineId": "script",
                    "scriptNodeId": "custom:script1"
                }
            }
        }
    }
}

And the script itself might look something like this:

function rendition(node) {
    return {
        "title": node.data.title + " Rendition",
        "headline": node.data.shortHeadline + " (Who would have thought?)",
        "format": "mobile"
    };
}

The resulting renditioned node JSON will look like:

{
    "title": "My Article Rendition",
    "headline": "Cubs win World Series! (Who would have thought?)",
    "format": "mobile"
}