Reports

All of the objects that you place into Cloud CMS can be operated against to produce exports that consist of
consolidated or reported information. You can use this capability to generate reports in CSV (comma-separated value)
format as well as merged PDFs and ZIP archives.

Exports are generated from collections of objects. These objects might be collected by hand or they might consist
of results sets from a document list, search page or report record set. The Cloud CMS user interface provides
methods for generating these collections and exporting them to a variety of formats.

Exported results can be downloaded, emailed or stored within Cloud CMS as a newly-created document.

An export essentially consists of the following steps:

  1. Start the Export. When an export is started, it is given a configuration that describes what to export and how to
    package up the results. The export itself runs as a background job. An exportId is made available to
    identify the export in the future to determine it's availability.

  2. Wait for the Export to Finish. Status checks are made using the exportId from the previous step.

  3. Do something with the Export's files. Something acquires or operates against the results of the export.
    For example, you might fetch the resulting ZIP document using its exportId.
    Or you might email the contents to a friend as an email attachment.

Export Configuration

The export configuration tells the export process what to work on and how to package up the results.

Common Properties

name type required description
references array true an array of reference strings for objects to be exported
package string false how to package results (either "CSV", "PDF", or "ZIP" - default is "ZIP")
includeMetadata boolean false Whether to include JSON of objects in report generation
includeAttachments boolean false Whether to include attachments of objects in report generation
includeAttachmentIds array false The string IDs of any attachments to be included
fields array false The dot-delimited string identifiers for any fields to include

The only required property is the references array which identifies which objects should be exported.
Objects are specified as references which generally follow the format:

{type}://{platformId}/{datastoreId}/{objectId}

If includeAttachmentIds is not specified, the "default" attachment will be exported. Typically, this is
what you will want. However, in some cases, you may want to export other attachment IDs or multiple attachments per
document.

If fields is not specified, all JSON fields will be included in the export.

CSV Exports

For CSV exports, no additional properties are required. The CSV export will automatically parse the property IDs for
each of the objects in your export and construct columns that are the union of properties across all objects. Object
properties are then placed into rows within the CSV export.

CSV exports do not support attachments. Thus, the includeAttachments and includeAttachmentIds
properties have no effect for this package type.

PDF Exports

For PDF exports, you can elect to have Cloud CMS auto-generate your PDFs using its built-in transformer subsystem or
you can specify a PDF generation template to use to customize the PDF build. You can also elect to merge generated
PDFs together (for multiple objects) into a single PDF output.

name type required description
pdfTemplateRepositoryId string false ID of the repository containing the PDF template
pdfTemplateBranchId string false ID of the branch containing the PDF template
pdfTemplateNodeId string false ID of the node for the PDF template
mergePdfs boolean false Whether to merge PDF entries into a single PDF

To specify a PDF generation template, use the pdfTemplateRepositoryId, pdfTemplateBranchId
and pdfTemplateNodeId properties to point to the Cloud CMS document (or node) that contains your PDF
generation template.

You can specify PDF generation templates using Freemarker, Handlebars and/or Markdown. You can also use .docx files
to generate PDFs using MERGE fields.

Use the mergePdfs property to tell the exporter to merge all of the generated PDFs together into a single
document. In this case, if you're using a PDF generation template, it will be run once with the full object set
for all objects at the same time.

ZIP Exports

For ZIP exports, you can have the exporter convert your ZIP entry files to PDF before zipping.

name type required description
zipUsePdfEntries boolean false whether to convert ZIP entries to PDF before zipping

By default, the entries of the ZIP file are not converted to PDF. The raw assets are zipped up, untouched.

Exporting

You can run an export like this:

// the platform
var platform = ...;

// assume we have a collection or result map of nodes
var nodes = ...;

// export a single PDF of all of the attachments
platform.runExport(nodes, {
    "package": "PDF",
    "mergePdfs": true,
    "includeMetadata": false,
    "includeAttachments": true
}, function(exportId, status) {

    // we now have the exportId!

});

This kicks off the export and waits for it to complete. Once it completes, the exportId is given to
you along with the status of the export.

Now that the export has completed, you can do things with the exported files.

Download the Export files

The export files can be accessed using simple URLs.

If the exported file is a single file, you fetch it like this:

/ref/exports/{exportId}/download

Or if the export produced multiple files, you fetch them like this:

/ref/exports/{exportId}/download/0
/ref/exports/{exportId}/download/1
...
/ref/exports/{exportId}/download/9

For any of these calls, you can also specify the download to come at you with the "Content-Disposition" header
flipped on. This allows the files to be saved instead of opened within the browser. Just add the following:

?a=true&filename=<filename>

Email the Export files

You email the files from a completed export like this:

// the export ID from an export that has completed
var exportId = ...;

// assume you have an email provider
var emailProvider = ...;

// email configuration
var emailConfig = {
    "to": "eddie@vanhalen.com",
    "from": "steve@vai.com",
    //"cc": "",
    "subject": "When is the new album coming out?",
    "body": "Keep on rocking"
};

emailProvider.sendForExport(exportId, emailConfig, function() {

    // all done!

});

Create a new Document from the Export files

You create a new document from a completed export's files like this:

// the export ID from an export that has completed
var exportId = ...;

// assume you have a branch
var branch = ...;

var exportConfig = {
    "properties": {
        "title": "Hello World"
    },
    "extraInfo": {
        "parentFolder": "/folder1/folder2"
    }
};

this.createForExport(exportId, exportConfig, function(response) {

    // all done!

});

Examples

Here are a few code examples that work end-to-end:

Search, Export and Download a CSV

// assume we have a branch
var branch = ...;

// assume we have a platform
var platform = ...;

branch.then(function() {

    // search for nodes
    var nodes;
    this.search("test").then(function() {
        nodes = this;
    });

    // export the nodes
    var exportId;
    this.subchain(platform).runExport(nodes, {
        "package": "CSV",
        "includeMetadata": true
    }, function(_exportId, _status) {
        exportId = _exportId;
    });

    // download the CSV
    // create node for export result
    this.then(function() {

        window.location.href = "/ref/exports/" + exportId + "/download?a=true&download=export.csv";

    });
});

Query, Export and Email a PDF

// assume we have a branch
var branch = ...;

// assume we have a platform
var platform = ...;

// assume we have an email provider
var emailProvider = ...;

branch.then(function() {

    // search for nodes
    var nodes;
    this.query({
        "rating": {
            "$gt": 3
        }
    }).then(function() {
        nodes = this;
    });

    // export the nodes
    var exportId;
    this.subchain(platform).runExport(nodes, {
        "package": "ZIP",
        "includeAttachments": true
    }, function(_exportId, _status) {
        exportId = _exportId;
    });

    // create node for export result
    this.then(function() {

        // email configuration
        var emailConfig = {
            "to": "joe@customer.com",
            "from": "sales@company.com",
            //"cc": "",
            "subject": "Your Product Information",
            "body": "Here is the product information you requested"
        };

        this.subchain(emailProvider).sendForExport(exportId, emailConfig, function() {

            // all done!

        });

    });

});

Find, Export and Create a ZIP

// assume we have a branch
var branch = ...;

// assume we have a platform
var platform = ...;

branch.then(function() {

    // search for nodes
    var nodes;
    this.find({
        "query": {
            "size": {
                "$in": ["sm", "md", "lg", "xl"]
            },
            "neck": {
                "$in": [16, 16.5, 17, 17.5]
            },
            "color": "green"
        },
        "search": "awesome"
    }).then(function() {
        nodes = this;
    });

    // export the nodes
    var exportId;
    this.subchain(platform).runExport(nodes, {
        "package": "ZIP",
        "zipUsePdfEntries": true,
        "includeAttachments": true
    }, function(_exportId, _status) {
        exportId = _exportId;
    });

    // create node for export result
    this.then(function() {
        var exportConfig = {
            "properties": {
                "title": "Any Size, Standard Neck, Green and Awesome!"
            },
            "extraInfo": {
                "parentFolder": "/users/user1/searches"
            }
        };
        this.createForExport(exportId, exportConfig, function(response) {

            // get the created node ID
            var id = response.rows[0]._doc;

            console.log("Created node ID is: " + id);
        });
    });
});

Export an Article as a PDF document

{
    "title": "Custom Article",
    "type": "object",
    "properties": {
        "title": {
            "type": "string",
            "title": "Title"
        },
        "body": {
            "type": "string",
            "title": "Body"
        },
        "author": {
            "type": "object",
            "properties": {
                "firstName": {
                    "type": "string",
                    "title": "First Name"
                },
                "lastName": {
                    "type": "string",
                    "title": "Last Name"
                },
                "email": {
                    "type": "string",
                    "title": "Email"
                }
            }
        }
    }
}

And let's imagine you have a content instance like this:

{
    "title": "Review of Star Trek II: The Wrath of Khan",
    "body": "An amazing film with a steady pace and intriguing storyline.",
    "author": {
        "firstName": "Opinionated",
        "lastName": "Critic",
        "email": "blah@blah.com"
    }
}

Let's generate a PDF for this content item. We can do so by creating another node (in this case, of type
n:pdf_template. This node doesn't need to have anything on it in terms of JSON or metadata, but it should
have an attachment which will hold our PDF template. Set the attachment mimetype to application/freemarker
and enter the following:

<#assign article=objects[0]>
<div class="container">
    <div class="row">
        <div class="col-12">

            <h2>${article.title}</h2>
            <p>
                By ${article.author.firstName} ${article.author.lastName}
            </p>
            <p>
                ${article.body}
            </p>

            <h3>Summary</h3>
            <table class="table table-bordered">
                <tbody>
                <#list article.highlights as highlight>
                <tr>
                    <td><b>${highlight.key}</b></td>
                    <td>${highlight.value}</td>
                </tr>
                </#list>
                </tbody>
            </table>
            <p>
                To learn more about this article, please
                <a href="https://www.mycompany.com" title="Our Web Site">visit our web site</a>.
            </p>
        </div>
    </div>
</div>

We can then tie everything together like this:

// assume we have a platform
var platform = ...;

// this is our PDF template node
var pdfTemplate = ...;

// this is the node we want to convert to pdf
var node = ...;

platform.then(function() {

    // export the nodes
    this.runExport([node], {
        "package": "PDF",
        "includeAttachments": true,
        "pdfTemplateRepositoryId": pdfTemplate.getRepositoryId(),
        "pdfTemplateBranchId": pdfTemplate.getBranchId(),
        "pdfTemplateNodeId": pdfTemplate.getId(),
        "mergePdfs": true
    }, function(exportId, status) {

        // redirect to our generated PDF
        window.location.href = "/proxy/ref/exports/" + exportId + "/download?a=true&download=export.pdf";
    });

});

The PDF template is given a model which consists of root-level objects. Each object is a node that we
pass in to the PDF template processor.

Controlling the Merge Sequence

If you set mergePdfs to true, then all of the nodes will be passed into a single
PDF generation process and a single PDF will be produced. If some of your nodes have attachments (such as Word
documents or other PDF documents) that you'd like be included as part of the merge, you can do so by specifying
the mergePdfsSequence.

The mergePdfsSequence array consists of string values which are references to nodes within Cloud CMS.
Node references take the structure:

node://<platformId>/<repositoryId>/<branchId>/<nodeId>

You can also use the special marker pdf to indicate where the merged PDF should be inserted.

Here is an example where we generate a PDF and then merge two additional PDFs into it (one in the front and one
in the back):

// assume we have a platform
var platform = ...;

// this is our PDF template node
var pdfTemplate = ...;

// this is the node we want to convert to pdf
var node = ...;

// front page PDF node
var frontPagePdfNode = ...;

// back page PDF node
var backPagePdfNode = ...;

platform.then(function() {

    // export the nodes
    this.runExport([node], {
        "package": "PDF",
        "includeAttachments": true,
        "pdfTemplateRepositoryId": pdfTemplate.getRepositoryId(),
        "pdfTemplateBranchId": pdfTemplate.getBranchId(),
        "pdfTemplateNodeId": pdfTemplate.getId(),
        "mergePdfs": true,
        "mergePdfsSequence": [
            frontPagePdfNode.ref(),
            "pdf",
            backPagePdfNode.ref()
        ]
    }, function(exportId, status) {

        // redirect to our generated PDF
        window.location.href = "/proxy/ref/exports/" + exportId + "/download?a=true&download=export.pdf";

    });

});