How-To Use Page Templates in Neowise and Build Scripts
1 Overview
Page templates and their elements are used to control product builds. You can turn any InDesign® document into a page template using the "Page Templates" panel in priint:comet desktop plugins. A page template can consist of zero to many page elements (frames). By creating frames in a page template document you automatically create page elements which can be enriched with more information (e.g. a label) using the Page Elements panel in priint:comet.
See priint:comet documentation for more details:
This article will explain how to set page templates in Neowise and later use them in the rendering of a document, executed using a Camunda process called from Neowise.
2 Assign a Page Template to a Publication or Folder in Neowise
In the Publication Window of Neowise, use the context menu to edit a publication or folder. Open the tab "Metadata" and the sub-tab "Page-Templates" to see the currently assigned page templates.
Click on the (+) button to assign new page templates. Use the (-) button of each row to remove already assigned page templates.
Use the filter at the top to filter for page template name. Select a page template by activating the checkbox and click the "Assign" button to finally assign the selected page template to a publication or folder.
3 Set a Page Template in Preparation Stage of a Document
3.1 Drag & Drop a Page Template to a Dynamic Document
In the Preparation Stage of a dynamic document it is possible to assign a page template, for later use in rendering scripts, by drag & drop from the list of available page templates on the right to the document on the left. The id of the page template is saved as the value of a document metadata with the key "_priint_pageTemplate".
3.2 Assign a Grid to a Page of a Static Document
see chapter 5 on how to prepare a page template as a grid to be used in static documents.
In the Preparation Stage of a static document it is possible to assign a grid (page template with special content metadata) by drag & drop from the list of available grids on the right to the document page on the left. The id of the grid is saved in the database table for document pages (puc_page).
4 Use a Page Template in List Planning
4.1 Render List Planning using Standard Build Process and Build Script
The Standard Camunda Rendering Process to use is "Run Renderer Script" (key: "P000_RunCScript").
In the service task "Run renderer script" it executes the build script selected (key: "scriptId") in the start form of the process.
The Service Task uses the rendering command "SCRIPT_META_DB" to inform the rendering service to execute a script ("SCRIPT"), create document metadata ("META") and register the new document in the database ("DB"). If you don't need document metadata or a registration of this document in the database, it is possible to use other rendering worker commands in your custom process. see here for more details about rendering work commands.
The standard build script for creating a document based on list plannings is "Build List Plannings" (1003). This build script reads the previously assigned document metadata "_priint_pageTemplate" and uses it as the page template for building the pages of the dynamic document.
/*
* Script Name : Build List Plannings
* Author : Gabriele Siegert
*
* (c) 2023 Werk II medien- und Informationsges. mbH, Philosophenweg 51, D-47051 Duisburg
*/
#include "internal/text.h"
#include "internal/products.h"
#include "internal/types.h"
int main() {
// Set here a fixed value > 0
// or the script will read the page template parameter _priint_pageTemplate
// where Neowise stores the page template for a dynamic document
//
int pageTemplateId = 0;
//
// OPTIONALLY:
// Build flags. Refer to the Comet InDesign Plugin documentation for
// possible values.
// Examples:
// int flags = kPreferExistingPages;
// int flags = kAutoDetectType + kLoadMasterItems;
// ...
//
int flags = 0; // = no build flags
//
// OPTIONALLY:
// we use the templates defined in the planning system.
// To override this, set defaultTemplateId to the ID of a valid
// template in your comet repository.
// This template will then be used for products with no template
// definition, or - depending on flags - for just all products.
//
int defaultTemplateId = 0;
//
// OPTIONALLY:
// Use a prescript to filter the product list loaded from the
// planning system.
//
int preScript = 0;
//
// please mind the ID mapping parameter (1 by default)
// 1 - refers to the PLANNING ID
// 0 - refers to the BUCKET ID
// 2 - uses compound BUCKET and PLANNING ID
int id_flag = 2;
//
// On which layer shall the templates be built?
// Set an index in build_layer_index (starting with 0 or higher),
// or set build_layer_index = -1 and the fixed layer name in build_layer
// or set build_layer_index = -2 and build_layer_parameter to a parameter identifier. The script will read the parameter's value and use it as layer name.
char * build_layer = alloc(255);
int build_layer_index = -2;
char * build_layer_parameter = "_priint_layer";
//
// On which page should the building start?
// Set either a fixed page, starting with 1, relative page number in the document,
// or for strting on the last existing page, use start_page = document::pages(gDocument)
int start_page = document::pages(gDocument);
//
// Here the implementation starts
//
ProductList prds = publication::get_document_product_planning(0, "", "", id_flag);
char * msg = alloc(10000);
char * tmp = alloc(10000);
int ret = 0;
wtlog("", "Build List Plannings START\n" );
wlog("","document: %s\n",document::path(tmp, gDocument));
if (productlist::length(prds)==0) {
strcat (msg, "No plannings found for this document, building not possible.");
createBusinessException (10,msg);
wlog("", "Build List Plannings ERROR END, gOutput = %s\n", gOutput);
return 0;
}
if (pageTemplateId==0)
pageTemplateId = readPageTemplateIdParameter(tmp, msg);
if (pageTemplateId==0) {
strcat (msg, "No page template assigned to this document, building not possible.");
createBusinessException (20,msg);
wlog("", "Build List Plannings ERROR END, gOutput = %s\n", gOutput);
return 0;
}
if (build_layer_index==-2) { // from parameter
ret = readLayerParameter(build_layer_parameter, msg, build_layer);
if (ret!=0) {
sprintf (msg, "Error reading layer parameter, building not possible.");
createBusinessException (30,msg);
wlog("", "Build List Plannings ERROR END, gOutput = %s\n", gOutput);
return 0;
}
} else if (build_layer_index>=0) { // choose layer by counting
if (build_layer_index >= layer::count()) {// not so many layers
sprintf (msg, "Not enough layers, found %d layers, layer index requested = %d, building not possible.", layer::count(), build_layer_index);
createBusinessException (40,msg);
wlog("", "Build List Plannings ERROR END, gOutput = %s\n", gOutput);
return 0;
}
layer::name(build_layer_index,build_layer);
}
wlog("","Establish settings: start_page=%d, build_layer=%s, pageTemplateId=%d, defaultTemplateId=%d, flags=%d, preScript=%d\n",
start_page, build_layer, pageTemplateId, defaultTemplateId, flags, preScript);
ret = productlist::establish(prds, 0, start_page, build_layer, pageTemplateId, defaultTemplateId, flags, preScript, 0, "", msg);
if (strlen(msg) || (ret != 0)) {
wlog("", "ERRORS:\n%s\nBUILD DONE WITH ERRORS %d %s IN DOCUMENT %s\n",
msg, ret, serror(ret), document::path(tmp, gDocument));
buildErrorHandling(ret,msg);
}
else {
ret = checkForFailedPlaceholders(msg);
if (ret!=0) {
wlog("", "ERRORS:\n%s\nBUILD DONE WITH ERRORS %d %s IN DOCUMENT %s\n",
msg, ret, serror(ret), document::path(tmp, gDocument));
createTechnicalException (ret, msg);
} else {
wlog("", "BUILD DONE SUCCESSFULLY IN DOCUMENT %s\n", document::path(tmp, gDocument));
}
}
wtlog("", "Build List Plannings END, gOutput = %s\n", gOutput);
release(msg);
release(tmp);
return 0;
}
int readLayerParameter(char * build_layer_parameter, char * msg, char * build_layer) {
int ret = 0;
String s;
ret = xml::get_document_attribute (gDocument, build_layer_parameter, build_layer);
if (ret != 0) { // error code returned
s = string::alloc("readLayerParameter error: %d %s ", ret, serror(ret));
strcat(msg, string::get(s));
string::release(s);
}
return ret;
}
int readPageTemplateIdParameter(char * tmp, char * msg) {
int pt_id = 0;
int ret = 0;
String s;
ret = xml::get_document_attribute(gDocument,"_priint_pageTemplate", tmp);
if (ret != 0) { // error code returned
s = string::alloc("readPageTemplateIdParameter error: %d %s tmp=%s ", ret, serror(ret), tmp);
strcat(msg, string::get(s));
string::release(s);
}
else {
if (strlen(tmp)>0)
pt_id = val(tmp);
}
return pt_id;
}
int createBusinessException (int ec, char * msg) {
String s = string::alloc("%d|BusinessError|%s",ec+10000,msg);
strcpy(gOutput,s);
return 0;
}
int createTechnicalException (int ec, char * msg) {
String s = string::alloc("%d|TechnicalError|%s",ec+50000,msg);
strcpy(gOutput,s);
return 0;
}
int buildErrorHandling(int ret,char * msg) {
if (ret==-127) {
createBusinessException (ret, msg);
} else {
createTechnicalException (ret, msg);
}
return 0;
}
int checkForFailedPlaceholders(char * msg) {
int state = 0;
int page;
LinkList lli = linklist::alloc();
Link li;
int ret = 0;
String s = string::alloc();
for (page=1; page <= document::pages(gDocument); page++) {
ret = linklist::collect(lli, page, "", kFirstIgnore, kSortNo);
wlog("","checkForFailedPlaceholders: Page %d: found %d placeholders (ret=%d).\n", page, linklist::length(lli), ret);
li = linklist::first (lli);
while (li) {
state = link::sync(li);
wlog("","checkForFailedPlaceholders: plId=%d state=%d\n", link::placeholderid(li), state);
if (state==-4) { // error state
ret = 4;
string::append(s,"Placeholder Load Error plId=%d stringId=%s, ", link::placeholderid(li), link::sid(li));
}
li = linklist::next(lli);
}
linklist::clear(lli); // prep for next page
}
if (ret!=0) {
strcpy(msg,s);
}
wlog("","checkForFailedPlaceholders ret=%d\n%s\n",ret, msg);
return ret;
}
4.2 Render List Planning using a Custom Build Process and Build Script
4.2.1 Custom Process
It is possible to create your own custom Rendering Process and reuse parts of the Standard Process by also copying parts of the process definition (BPMN diagram). If you e.g. don't need to select a script in your custom process, it is also possible to hard code the script id in the rendering service task and to remove the start form definition from the start event of your custom process.
4.2.2 Custom Build Script
To create a custom build script use the standard build script as a base and change it. Hard code or select your new script id in your custom process or select it in the standard process "Run Renderer Script".
5 Prepare Page Templates to be used as Grids for Grid Planning
5.1 Create Content Metadata in ISON
In your entity model you need some special content metadata entities.
Entity Identifier | Description |
---|---|
grid | value ("yes" or "no") for key "grid" controls whether a page template is used as a grid or not. |
move_resize | values control (moving and) resizing of grid elements. The only used key is "grid_element_resize". "grid_element_overlapping" and "grid_element_move" are ignored! |
content_creation | Not used! Will be replaced with grid element permissions in a future release. |
Use the Mua connector for these entities. Currently the default values of metadata for a grid and grid elements are not supported by the installer. Therefore, some config files must be created by hand in the repository explorer for your comet project.
The folder path “…./metadata/predefined_values” must exist. If not create this folder path with the context menu:
To create config files use “New Config File”. Type of page_templates.xml is CometPageTemplatePredefinedMetadata, type of page_template_elements.xml is CometPageTemplateElementPredefinedMetadata.
If you created the config files you can copy & paste the definition, which is delivered here. page_templates.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<con:PluginConfig xmlns:con="com.priint.pubserver.config.manager/20130620" xmlns:dat="com.priint.pubserver.plugin.entitydata/20131216" xmlns:comet="com.priint.comet.entity/20130823" xmlns:pl="com.priint.pubserver.plugin/20140213" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tags="com.priint.pubserver.tagging/20161102">
<con:name>page_templates.xml</con:name>
<con:type>CometPageTemplatePredefinedMetadata</con:type>
<con:description>536896635 [CometPageTemplatePredefinedMetadata]</con:description>
<con:custom>
<comet:pageTemplatePredefinedMetadata>
<comet:id>536896635</comet:id>
<comet:metadataPredefinedValues>
<comet:name>default</comet:name>
<comet:metadata>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="grid" groupIdentifier=""
identifier="5f511342-d96f-4a52-8833-7f8367895d31" key="grid" origin=""
sequence="0" timestamp="" updatedBy="" value="yes">
<dat:contentMetaDataList/>
</comet:contentMetaData>
</comet:metadata>
</comet:metadataPredefinedValues>
</comet:pageTemplatePredefinedMetadata>
</con:custom>
<con:dependencies>
<con:PluginDependency name="/pubserver/pluginconfig/com.priint.pubserver.comet.config.CometConfiguration/qad/custom/aio/pagetemplate/metadata/predefined_values/index.xml" restriction="PARENT"/>
</con:dependencies>
<con:instances/>
</con:PluginConfig>
page_template_elements.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<con:PluginConfig xmlns:con="com.priint.pubserver.config.manager/20130620" xmlns:dat="com.priint.pubserver.plugin.entitydata/20131216" xmlns:comet="com.priint.comet.entity/20130823" xmlns:pl="com.priint.pubserver.plugin/20140213" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tags="com.priint.pubserver.tagging/20161102">
<con:name>page_template_elements.xml</con:name>
<con:type>CometPageTemplateElementPredefinedMetadata</con:type>
<con:description>536884121 [CometPageTemplateElementPredefinedMetadata]</con:description>
<con:custom>
<comet:pageTemplateElementPredefinedMetadata>
<comet:id>536884121</comet:id>
<comet:metadataPredefinedValues>
<comet:name>Flexible gridelement</comet:name>
<comet:metadata>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="content_creation" groupIdentifier="" identifier="4b9f9b82-f03f-45a7-8d02-c1a0222ff81d" key="content_creation_new" origin="" sequence="1" timestamp="" updatedBy="" value="no">
<dat:contentMetaDataList/>
</comet:contentMetaData>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="content_creation" groupIdentifier="" identifier="dd7c6dde-be8a-40a1-9f60-f44a77686681" key="content_creation_bucket" origin="" sequence="2" timestamp="" updatedBy="" value="">
<dat:contentMetaDataList/>
</comet:contentMetaData>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="content_creation" groupIdentifier="" identifier="47efeb00-1eb7-42fc-be17-68590050cd70" key="content_creation_wizard" origin="" sequence="3" timestamp="" updatedBy="" value="_EditRealGrid.xml">
<dat:contentMetaDataList/>
</comet:contentMetaData>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="move_resize" groupIdentifier="" identifier="4f5f1c8c-315d-4ccc-8059-0a6fcf8fab8a" key="grid_element_resize" origin="" sequence="10" timestamp="" updatedBy="" value="yes">
<dat:contentMetaDataList/>
</comet:contentMetaData>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="move_resize" groupIdentifier="" identifier="78bbacac-919c-4179-8b76-b78721c9bfce" key="grid_element_overlapping" origin="" sequence="20" timestamp="" updatedBy="" value="yes">
<dat:contentMetaDataList/>
</comet:contentMetaData>
<comet:contentMetaData createdBy="" dataType="" entityContentMetaDataId="move_resize" groupIdentifier="" identifier="257e9259-d756-4d46-ad54-f20a2048cf0c" key="grid_element_move" origin="" sequence="30" timestamp="" updatedBy="" value="Document">
<dat:contentMetaDataList/>
</comet:contentMetaData>
</comet:metadata>
</comet:metadataPredefinedValues>
</con:custom>
<con:dependencies>
<con:PluginDependency name="/pubserver/pluginconfig/com.priint.pubserver.comet.config.CometConfiguration/qad/custom/aio/pagetemplate/metadata/predefined_values/index.xml" restriction="PARENT"/>
</con:dependencies>
<con:instances/>
</con:PluginConfig>
5.2 Configure Page Template as a Grid in ISON
The page template which is becoming a grid can be also used as a regular page template. To set a page template to be used as a grid you must execute the following steps:
Do the following steps:
- Select the page template in the Comet Explorer
- Call the context menu “Apply predefined metadata”
- A wizard opens
- Select the item “default”
- Click on “Next”
- If everything is ok the page template is not red marked. If the page template is red marked, this means, that meta data are already assigned to the page template.
- Click on “Finish”
5.3 Configure Grid Elements (Page Elements) in ISON
Each single grid element element can be configured. If not configured, Neowise will use default values.
- Select page template in the Comet Explorer
- Call context menu “Edit”
- Select “Elements”
- Select single grid element “1”
- Unfold folder “move_resize”
- Select “grid_element_resize”
- Call context menu “Edit”
- Enter possible value, in that case “yes” or “no”
- Click “OK”
As follows you can see a list of all configuration possibilities for single grid elements:
move_resize:
grid_element_resize:
- values: yes, no
- default: yes
- description: controls if a grid element can be resized
5.4 Summary
In Neowise only the ContentMetadata key grid_element_resize (entity move_resize) is taken into account. The entity content_creation with it's keys and the other keys from the entity move_resize (grid_element_overlapping and grid_element_move) known from "Layout Briefing" are ignored. A configuration that needs to be added to the Page Template is the Content Metadata Flag grid=yes.
6 Use a Page Template in Grid Planning
6.1 Assign Grid to a Page in Grid Planning
In addition to setting a grid of a page in the Preparation Stage of a static document, it is also possible to set or change a grid in the Content Assignment Stage of a static document. This is also called "Grid Planning". This is done in the same way as in the Preparation Stage by drag & drop a grid to a page.
6.2 Render Grid Planning using the Standard Build Process and Build Script
The Standard Camunda Rendering Process to use is "Run Renderer Script" (key: "P000_RunCScript").
In the service task "Run renderer script" it executes the build script selected (key: "scriptId") in the start form of the process.
The Service Task uses the rendering command "SCRIPT_META_DB" to inform the rendering service to execute a script ("SCRIPT"), create document metadata ("META") and register the new document in the database ("DB"). If you don't need document metadata or a registration of this document in the database, it is possible to use other rendering worker commands in your custom process. see here for more details about rendering work commands.
The standard build script for creating a document based on grid plannings is "Grid: Page Generation (Camunda)" (1018). This script directly uses the "grid_build" command from the priint:comet plugins.
/* Script name: standard page generation script
* Configuration version: 4.3.0.R1
*
* This script is used to generate pages with adaptive
* page templates.
* For details see the planningfunctions.h script library
* You can adapt this script to your personal requirements.
* Preconditions for utilizing this script:
* - a planner document is opened and active
* - gOutput and gDocumentID are defined as global variables
* When run in Camunda context, these variables are already
* defined.
*/
#include "[pubserver]/planningfunctions.h"
int main() {
return generate_grid_pages(gDocumentID, gOutput);
}
/* Script name / purpose: common functions for
* - generating previews of grid plannings
* - generating pages of grid plannings
* Configuration version: 4.4.0.R1
*
* DO NOT CHANGE THIS SCRIPT.
* Changes might be overriden the next time you start up
* your PublishingServer. To adapt this script, rather create
* a copy and include the new script library.
*
* (c) 2023 Werk II GmbH, Philosophenweg 51, D-47051 Duisburg
*/
#include "internal/panels.h"
#include "internal/products.h"
#include "internal/types.h"
#include "[pubserver]/plugin/plannerlib.c"
int build_grid(char * documentId, char * realGridElementId, char * output) {
// 1st: get plannings of the real grid element
PlanningList gridPlannings = planning::get_grid_plannings(documentId, realGridElementId);
int buildResult = 0;
char * buildMessage = alloc(4096);
char * errorClass = alloc(4096);
int i = 0;
ItemList allItems = 0;
int groupId = 0;
JSON result = json::alloc();
// 2nd: call new build function
buildResult = productlist::grid_build(
gDocument, // document ref
gridPlannings, // list of plannings to build
0, // flags can be any of the flags supported
// by productlist::establish (though most don't
// make sense in this context).
// Try
// - 0: ignore grid element size, propably useful
// for the previews
// - kCheckGridElementSize: check grid size, useful
// for 'real' page generation
buildMessage // buffer for build error messages, default = 0
);
// At this point, we could check for buildResult: values other than 0 indicate a
// build error or problem. We could consider aborting the operation here - or just
// continue and check, if there is at least some build result.
//
// Return value 111103 indicates, that the products simply do not fit the real
// grid element
if (!buildResult || (buildResult == 111103)) {
// 3d: assemble frames of all plannings and create comet group
allItems = itemlist::alloc();
for (i = 0; i < publication::planninglist::length(gridPlannings); ++i) {
ItemList planningItems = itemlist::alloc();
Planning planning = publication::planninglist::get(gridPlannings, i);
// After building, frames created for a planning are attached to this planning
// object and can be retrieved as follows:
publication::planning::get_itemlist(planning, kPlanningOriginals, planningItems);
// We could check, if any frame was inserted for this planning. If not, this could
// also be considered an error:
// if (itemlist::length(planningItems) == 0) {
// // do some error handling...
// }
itemlist::appendlist(allItems, planningItems);
itemlist::release(planningItems);
}
if (itemlist::length(allItems) > 0) {
buildResult = 0;
groupId = itemlist::create_cometgroup(allItems);
}
else {
buildResult = 1116; // "item empty", more or less reasonable to indicate that kind of error.
}
}
if (buildResult == 0) {
strcpy(errorClass, "Success");
}
else {
strcpy(errorClass, "BusinessError");
}
json::set_error (result, buildResult, errorClass, buildMessage);
json::set_int (result, "gridPreviewCometGroupId", groupId);
json::serialize (result, output);
release (buildMessage);
release (errorClass);
json::release (result);
return 0;
}
int generate_grid_pages(char * documentId, char * output) {
// 1st: get plannings of the document
PlanningList gridPlannings = planning::get_document_grid_plannings(documentId);
int buildResult = 0;
char * buildMessage = alloc(4096);
char * errorClass = alloc(4096);
JSON result = json::alloc();
// 2nd: call new build function
buildResult = productlist::grid_build(
gDocument, // document ref
gridPlannings, // list of plannings to build
0, // flags can be any of the flags supported
// by productlist::establish (though most don't
// make sense in this context).
// Try
// - 0: ignore grid element size, propably useful
// for the previews
// - kCheckGridElementSize: check grid size, useful
// for 'real' page generation
buildMessage // buffer for build error messages, default = 0
);
if (buildResult == 0) {
strcpy(errorClass, "Success");
}
else {
strcpy(errorClass, "BusinessError");
}
json::set_error (result, buildResult, errorClass, buildMessage);
json::serialize (result, output);
release (buildMessage);
release (errorClass);
json::release (result);
return 0;
}
/*
* Can be used to test grid build in InDesign Desktop
* Preconditions:
* - publication document is opened and active ("front document")
* - one or several "real grid element" entries are selected in
* the product pool
*/
int main_Desktop()
{
IDTypeList nodes = idtypelist::alloc(kPanelProducts, kSelected);
IDType tmp;
char * tmpId = alloc(4096);
char * gridId = alloc(4096);
char * output = alloc(4096);
for (tmp = idtypelist::first(nodes); tmp; tmp = idtypelist::next(nodes)) {
strcpy(tmpId, idtype::stringid(tmp));
strcpy(gridId, idtype::record_id(tmpId));
wlog("", "Building for %s\n", gridId);
build_grid(gDocumentID, gridId, output);
}
return 0;
}
/**
* Can be used for Neowise / Camunda integration
* Preconditions:
* - a planner document is opened and active
* - gRealGridElementID is set as global variable
* 'gOutput' and 'gDocumentID' are always defined, when
* running scripts in that context, so we don't need to
* bother that.
*/
int main_Camunda() {
return build_grid(gDocumentID, gRealGridElementID, gOutput);
}
6.2 Render Grid Planning using the a Custom Build Process and Build Script
6.2.1 Custom Process
It is possible to create your own custom Rendering Process and reuse parts of the Standard Process by also copying parts of the process definition (BPMN diagram). If you e.g. don't need to select a script in your custom process, it is also possible to hard code the script id in the rendering service task and to remove the start form definition from the start event of your custom process.
6.2.2 Custom Build Script
To create a custom build script use the standard build script as a base and change it. Hard code or select your new script id in your custom process or select it in the standard process "Run Renderer Script". If you don't want to use the build-in functionality of creating documents based on grid plannings you can also try to use the approach of adaptive templates. See here for more details.