Introduction

Using the new Planning & Briefing capabilities in the priint:planner 4.1 allows a more flexible way to set up publications and documents.
This Howto describes, how to use planning data in the priint:comet plug-ins resp. priint:pdf renderer. We focus on configuration and Java development aspects.

Requirements

Working with planning data requires priint:publishing server and priint:comet plug-insVersion 4.1. Please use the latest available release; while basic functionality should work with any 4.1 version, the full functionality as described in this documentation requires

Examples

The following examples use standard DataMapping and DataProcessing methods delivered with priint:publishing server.
We assume, that an entity model and Comet project already exist, also some example data would be nice. In that regard, the output on your system probably will differ from the result shown in the examples.
The configuration of all other resources (DataProvider, placeholder, templates, Java methods) is described in the section Example implementation, so at least it should be possible to achieve equivalent results in your environment.

Preparation

(1) To keep it simple, we use the same document in all of the following examples.
The first step should be, to create a publication and a document in the priint:planner. Once the document has been created, open he planning panel by right-clicking on the document:

Open Planning

(2) In the right area of the Planning panel, you see content and configuration from your data source and comet project, in the left area you see, what is actual planned for this document.
By drag and drop content to the left area, you can add content to the document:

(3) Content added to the document can be configured for this particular document, e.g. use different template, only select certain attributes / sub nodes etc.
When finished, click the Save button to persist the planning data for this document.

(4) That's all for configuration.
Switch to InDesign, connect to the priint:publishing server, open the Publication Panel and check out the document just configured:

Standard Build with Planning Data

The first example uses a find statement, which is part of the standard delivery, and the standard priint:comet build function.

You see the four buckets (or maybe different data, depending on your selection in the first step), we just configured for this document.

You can select these items, assign a template and use the Build button at the bottom of the panel to add these products to the document.
Not very surprisingly, you will see a result similar like this:

We now configure a Multiframe placeholder (details see in the Example implementation section), this placeholder loads all sub buckets of a product and inserts the same "Product label" template for each of them.

The result shows labels for each of the three child buckets of the product, we selected.
Remember: in the Planning panel, we only selected two of the child buckets, therefore, what we see in the first example, is simply the result of the standard Build, Planning just serves as kind of customizable find method.

Multiframe with Planning Sub Elements

This example requires some additional plug-ins, which are shipped as Demo projects. Code is shown below in the Example implementation section.

We select another find statement from the Method drop down menu: "Find document plannings". As obvious, the list shows more or less the same content:

We link the frame to another placeholder, we just configured for that purpose (again, implementation details can be found in the Example implementation section), choose the same "Product Label" template for sub elements and link again with the first entry in the product list.

After selecting "Create sub elements of selected products", we see the following result:

Table Composition based on Planning Data

Similar to the Mutliframe example, we can use planning data in table insert methods, i.e.: generate rows and columns based on the Planning hierarchy, rather than the product structure.

Based on a data provider, which returns the child buckets of a planning record as RecordIds, we configure a table insert method, which we use to copy rows of this very simple table (implementation details can be found in the Example implementation section):

We link this table to the first entry of the "Find document plannings" result list, click into the first (and only) table cell and select "Build complete table" from the Table composition panel menu.

This will lead to the following result:

Again, not that exiting, just what we expected: a row resp. cell has been generated for each child bucket of the planning record.

Example Implementation 

Standard Build with Planning Data 

Step by Step:

Remarks

This example just demonstrates, that in InDesign / priint:comet you can work with planning data almost like with other entity data.

If you take a closer look to the results shown in the Product pool panel, you will notice, that the IDs provided with the product data refer to the Bucket linked with the planning record - not to the planning record itself.

This is, because the find statement used here returns a list of Buckets and in this case the CometBridge standard mapping is used to transform planning data to priint:comet product items

The same mapping is used e.g. for the cscript publication::get_document_product_planning function.

Extract of the find statement method used in this example

  @PubServerMethod(
      type=PluginMethod.MethodType.FIND_STATEMENT,
      label="Find planned products",
      description="Find all products planned to given Document in Planning & Briefing")
  public List<Bucket> findPlannedProducts (
      @PubServerMethodParameter(
               name="modelId", 
               defaultValue="<Model.Id>") String modelId,
      @PubServerMethodParameter(
               name="context", 
               defaultValue="<Entity.Context>") Context context) 
                                                         throws CometException {
    
    List<Bucket> result = new ArrayList<Bucket>();
    // ... 
    try {
      EntityManagerLocal em = getEntityManager();
      List<Planning> plannings = em.getPlanningByDocument(sessionId, 
                                                          modelId, 
                                                          documentID, 
                                                          "", "");
      for (Planning p : plannings) {
        List<Bucket> buckets = em.getEntityBucketsByIdentifier(sessionId, 
                                                                modelId, 
                                                                p.getBucketId(), 
                                                                p.getEntityBucketId(), 
                                                                null, "");
        if (buckets != null) {
            result.addAll(buckets);      
        }          
      }
    }
    catch (Exception e) {
      throw new CometException(this, UNABLE_TO_FIND_BUCKETS + e.getMessage(), e);
    }
    return result;
  }
    

Multiframe with Planning Sub Elements 

This example is based on a custom find statement, which returns the Planning record Ids. To retrieve the child elements, we use the standard getEntityPlanningsByIdentifier query method and a custom mapping method, which returns the Bucket children as Elements. Code for the find statement and mapping method is shown below. Step by Step configuration:

Custom find statement implementation

For detailed information about find statements, refer to the Find statements Howto.
One key feature is, that find statements can return either a list of Buckets or a list of priint:comet products. The latter allows to use an arbitrary mapping for the find results, in this case we create products using the Planning record identifier.

Note the Mappingutils method call to create a correct string identifier for the planning record. This is part of the CometBridgeSDK, which provides several methods for any type of entity data. Whenever possible, methods from the SDK should be used.
The second parameter (in this case null) allows to include parent information; this is required, if your placeholder needs to access parent properties to load properly. In most cases, this is not necessary.

package com.priint.pubserver.comet.findstatement;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.priint.pubserver.comet.bridge.entitydata.Product;
import com.priint.pubserver.comet.bridge.util.MappingUtils;
import com.priint.pubserver.comet.exception.CometException;
import com.priint.pubserver.plugin.PluginControlDefault;
import com.priint.pubserver.plugin.PluginMethod;
import com.priint.pubserver.plugin.PluginUtils;
import com.priint.pubserver.plugin.annotation.PubServerMethod;
import com.priint.pubserver.plugin.annotation.PubServerMethodParameter;
import com.priint.pubserver.plugin.annotation.PubServerPlugin;
import com.priint.pubserver.plugin.entitydata.Context;
import com.priint.pubserver.plugin.entitydata.Planning;
import com.priint.pubserver.plugin.interfaces.EntityManagerLocal;
import com.priint.pubserver.util.Constants;

@Stateless(mappedName=DemoPlanningStatement.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class DemoPlanningStatement extends PluginControlDefault {
  public static final String MAPPED_NAME = 
            "com.priint.pubserver.comet.findstatement.DemoPlanningStatement";

  private static final Logger LOGGER =
            LoggerFactory.getLogger(DemoPlanningStatement.class);
    
  private EntityManagerLocal getEntityManager() throws CometException {
    String sessionId = PluginControlDefault.getSessionId();
    return PluginUtils.getPlugin(Constants.MANAGER_ENTITY,
                                 sessionId,
                                 EntityManagerLocal.class);
  }

  @PubServerMethod(
      type=PluginMethod.MethodType.FIND_STATEMENT,
      label="Find document plannings",
      description="Find planning records to given document in Planning & Briefing")
  public List<Product> findPlannedProducts (
      @PubServerMethodParameter(name="modelId", 
                                defaultValue="<Model.Id>") String modelId,
      @PubServerMethodParameter(name="context", 
                                defaultValue="<Entity.Context>") Context context)
                                throws CometException {
    
    List<Product> result = new ArrayList<Product>();
    
    String sessionId  = getSessionId();
    String documentId = context.getDocumentId(); 
    
    if (documentId!=null && !documentId.isEmpty()) {
      try {
        EntityManagerLocal em = getEntityManager();
        List<Planning> plannings = em.getPlanningByDocument(sessionId,
                                                                  modelId,
                                                                  documentId,
                                                                  "", "");
        
        if (plannings != null) {
          for (Planning planning : plannings) {
            Product product = new Product();
            product.setId(1); // 1st ID must be nonzero
            product.setStringId(MappingUtils.createPlanningId(planning, null));
            product.setColumn1(planning.getLabel());
            product.setColumn2("PLANNING");
            result.add(product);
          }
        }
      } 
      catch (Exception e) {
        throw new CometException(this, 
                                "Document Id is null or empty." + e.getMessage(), 
                                e);
      }
    }
    else {
      LOGGER.error("DocumentId is empty!");
    }
    return result;
  }
}
    

Custom mapping method implementation

Multiframe placeholders require a data mapping method, which returns a list of priint:comet Elements to load sub elements. In our mapping method, we get the sub plannings from the Planning record retrieved from the data query method and map this to elements.
In contrary to the find statement above, we use the Bucket identifiers for the resulting elements, rather than the Planning identifier.

private InitialContext initialContext = null;

private DataMappingToElementLocal getElementDataMapping() {
  try {
    if (initialContext == null) { 
      initialContext = new InitialContext();
    }
    return (DataMappingToElementLocal)initialContext.lookup(
                DataMappingToElementLocal.JNDINAME);
  } catch (NamingException e) {
    LOGGER.error(
        "Failed to lookup Element Data Mapping, reason='" + e.getMessage() + "'", 
        e);
    initialContext = null;
  }
  return null;    
}

private EntityManagerLocal getEntityManager() throws CometException {
  String sessionId = PluginControlDefault.getSessionId();
  return PluginUtils.getPlugin(Constants.MANAGER_ENTITY, 
                                                                sessionId, 
                                                                EntityManagerLocal.class);
}

@PubServerMethod(type=PluginMethod.MethodType.DATA_MAPPING)
public List<Element> getSubPlanningAsElements(
    @PubServerMethodParameter(name="in",
                              description="result from query",
                              defaultValue=ParameterTags.Tag.ENTITY_RESULTLIST) 
                                List<Planning> in,
    @PubServerMethodParameter(name="entityModel", 
                              description="Entity Model",
                              defaultValue=ParameterTags.Tag.MODEL_ID) 
                                String entityModel,
    @PubServerMethodParameter(name="context",
                              description="context",
                              defaultValue=ParameterTags.Tag.ENTITY_CONTEXT) 
                                Context context
    ) {
  List<Element> result = new ArrayList<Element>();
  
  DataMappingToElementLocal mapping = this.getElementDataMapping();
  
  for (Planning p : in) {
    for (Planning subPlanning : p.getListPlanning()) {
      try {
        EntityManagerLocal em = this.getEntityManager();
        
        List<Bucket> buckets = em.getEntityBucketsByIdentifier(
            PluginControlDefault.getSessionId(),
            subPlanning.getEntityModelId(),
            subPlanning.getBucketId(),
            subPlanning.getEntityBucketId(),
            context,
            "");
        
        if (buckets != null && !buckets.isEmpty()) {
          result.addAll(mapping.bucketsToElements(buckets, entityModel, context));
        }
      } catch (EntityManagerException | CometException e) {
        e.printStackTrace();
      }
    }
  }
  return result;
}
    

Table Composition based on Planning Data 

The table composition example is just a variant of the Multiframe example above. We use the same custom find statement, the same query method in the data provider and a very similar mapping method: Step by Step configuration:

As said before, the technical requirements for this example are very similar to the Multiframe example above. The mapping method used in the data provider is slightly different, because table insert methods require a list of RecordIds as return type:

private InitialContext initialContext = null;

private DataMappingToRecordIdLocal getRecordIdDataMapping() {
  try {
    if (initialContext == null) { 
      initialContext = new InitialContext();
    }
    return (DataMappingToRecordIdLocal)initialContext.lookup(
                DataMappingToRecordIdLocal.JNDINAME);
  } catch (NamingException e) {
    LOGGER.error(
        "Failed to lookup RecordId Data Mapping, reason='" + e.getMessage() + "'", 
        e);
    initialContext = null;
  }
  return null;    
}

private EntityManagerLocal getEntityManager() throws CometException {
  String sessionId = PluginControlDefault.getSessionId();
  return PluginUtils.getPlugin(Constants.MANAGER_ENTITY, 
                                                                sessionId, 
                                                                EntityManagerLocal.class);
}

@PubServerMethod(type=PluginMethod.MethodType.DATA_MAPPING)
public List<RecordId> getSubPlanningAsRecordIds(
    @PubServerMethodParameter(name="in",
                              description="result from query",
                              defaultValue=ParameterTags.Tag.ENTITY_RESULTLIST) 
                                List<Planning> in,
    @PubServerMethodParameter(name="context",
                              description="context",
                              defaultValue=ParameterTags.Tag.ENTITY_CONTEXT) 
                                Context context
    ) {
  List<RecordId> result = new ArrayList<RecordId>();
  
  DataMappingToRecordIdLocal mapping = this.getRecordIdDataMapping();
  
  for (Planning p : in) {
    for (Planning subPlanning : p.getListPlanning()) {
      try {
        EntityManagerLocal em = this.getEntityManager();
        
        List<Bucket> buckets = em.getEntityBucketsByIdentifier(
            PluginControlDefault.getSessionId(),
            subPlanning.getEntityModelId(),
            subPlanning.getBucketId(),
            subPlanning.getEntityBucketId(),
            context,
            "");
        
        if (buckets != null && !buckets.isEmpty()) {
          result.addAll(mapping.bucketsToRecordIds(buckets));
        }
      } catch (EntityManagerException | CometException e) {
        e.printStackTrace();
      }
    }
  }
  return result;
}

Cscript API for Planning Data

The easiest way to use planning data in cscript is using the publication::get_document_product_planning function. This function returns the list of products planned for this document using the standard data mapping, i.e.: use the Bucket identifiers for the string Ids, rather than the identifier of the planning record.
The benefit is, that the result of this function call can be used for common build functions like this:

  ProductList products = publication::get_document_product_planning();
  productlist::establish(products,
                         0, 
                         document::pages(gDocument), 
                         "", 
                         pageTemplateId, 
                         defaultTemplateId, 
                         flags, 
                         preScript, 
                         0, 
                         "", 
                         msg);
The drawback is, that thus only the first level of the planning is respected. All other data is loaded according to the product hierarchy.

From R20546, an addtional parameter is supported, which allows to retrieve the Planning IDs rather than the IDs of the bucket linked with a planning in the result of the publication::get_document_product_planning function.

  publication::get_document_product_planning (
    char * documentId   = 0,  // documentId, defaults to current document
    char * entityId     = "", // result entity, defaults to empty (= all)
    char * searchString = "", // search strings: search criteria for result records
    int mappingFlag     = 0   // flag for ID mapping
                              //   0 = use bucket ID (default)
                              //   1 = use planning ID
                              //   2 = use bucket ID aand planning ID as parent
    );  

In the priint:comet plug-ins 4.1 R19501, new cscript data types have been introduced, which allow to load and process planning data in cscript. In conjunction with the Java / cscript bridge, this allows more flexible ways to set up product lists or placeholders to use planning data rather than product data.
More information can be found in the Java / cscript Howto and in the cscript Online-documentation shipped with priint:comet plug-ins.
The new data types are:

Planning        // single planning record
PlanningList    // a list of planning records
New functions introduced to process planning data are
// Planning
publication::planning::alloc          // create a new planning record
publication::planning::copy           // copy a planning record
publication::planning::release        // release a planning record
publication::planning::get            // get an int property of a planning record
publication::planning::gets           // get a string proeprty of a planning record
publication::planning::getlist        // get a list property of a planning record
publication::planning::getfloat       // get a float property of a planning record
publication::planning::to_xml         // serialze a planning record to XML
publication::planning::from_xml       // unserialize a planning record from XML

// PlanningList
publication::planninglist::add_all    // add all elements of one list to the other
publication::planninglist::alloc      // create a new planning list
publication::planninglist::release    // release a planning list
publication::planninglist::length     // get the number of entries
publication::planninglist::get        // get a particular planning entry
publication::planninglist::to_xml     // serialize list to XML
publication::planninglist::from_xml   // unserialize list from XML
Please refer to the plug-ins Online-documentation for detailed information.

DataQuery Methods for Planning Data

All methods are part of the EntityManagerRemote resp. EntityManagerLocal interface (package com.priint.pubserver.plugin.interfaces) and implemented in the PubServerEntityManager Plugin.

For detailed description including parameter documentation, please refer to the PubServerSDK Java documentation.

getEntityChildBucketsOfPlanning

public List<Bucket> getEntityChildBucketsOfPlanning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of buckets linked to sub plannings of a planning record.

getEntityCordedBucketsOfPlanning

public List<Bucket> getEntityCordedBucketsOfPlanning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of target buckets of planned cords of a planning record.

getEntityKeyValuesOfPlanning

public List<KeyValue> getEntityKeyValuesOfPlanning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of planned KeyValues of the bucket linked to a planning record.

getEntityMediaAssetsOfPlannning

public List<MediaAsset> getEntityMediaAssetsOfPlannning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of planned MediaAssets of the bucket linked to a planning record.

getEntityPricesOfPlannning

public List<Price> getEntityPricesOfPlannning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of planned Prices of the bucket linked to a planning record.

getEntityTableDataOfPlannning

public List<TableData> getEntityTableDataOfPlannning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of planned TableData of the bucket linked to a planning record.

getEntityTextsOfPlannning

public List<Text> getEntityTextsOfPlannning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of planned Texts of the bucket linked to a planning record.

getEntityPlanningsOfDocument

public List<Planning> getEntityPlanningsOfDocument(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String resultEntityId,
      final Context context,
      String searchStr) throws EntityManagerException;

Returns the plannings of a particular document.

getEntityPlanningsByIdentifier

public List<Planning> getEntityPlanningsByIdentifier(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String resultEntityId,
      final Context context,
      String searchStr) throws EntityManagerException;

Returns the planning record with the Id provided as parameter.

getEntityPlanningsOfPage

public List<Planning> getEntityPlanningsOfDocument(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String resultEntityId,
      int pageIndex,
      final Context context,
      String searchStr) throws EntityManagerException;

Returns the plannings of a page.

getEntitySubPlanningsOfPlanning

public List<Planning> getEntityTextsOfPlannning(
      String sessionId,
      String entityModelIdentifier,
      String entityId,
      String parentPlanningId,
      String resultEntityId,
      final Context context,
      String records,
      String searchStr) throws EntityManagerException;

Returns the list of subplannings of a planning record.

DataMapping Methods for Planning Data

planningPropertyToString

List<String> planningPropertyToString(List<Planning> plannings, String propertyName);
Gets an arbitrary Planning property as String.
The property name can either be set in the dataprovider configuration in ISON or can be defined as a function variable in a generic placeholder. The list of available properties is set up using the getPlanningProperties value list method, see below.

planningsToRecordIds

List<RecordId> planningsToRecordIds(List<Planning> plannings, Context context);
Maps Planning records to RecordIds using the standard mapping. The RecordIds refer to the Bucket entities linked to this Planning record.

planningsToRecordIdsGeneric

List<RecordId> planningsToRecordIdsGeneric(
        List<Planning> plannings,
        Context context,
        String groupNameProperty);
Maps Planning records to RecordIds using the standard mapping. The RecordIds refer to the Bucket entities linked to this Planning record.
In addition, the groupName field can be set to any property of the Planning record. This can be configured in ISON or defined as a function variable in a generic placeholder. Available properties are set up using the getPlanningProperties value list method also.

planningsToPlanningRecordIds

List<RecordId> planningsToPlanningRecordIds(
        List<Planning> inputList,    
        Context context,
        String idProperty,
        String id2Property,
        String id3Property,
        String stringIdProperty,  
        String groupNameProperty)

Maps Planning records to RecordIds using the standard mapping. The RecordIds refer to the Planning record.
All properties of the result RecordIds can optionally be set to any property of the planning record using the idProperty, id2Property, id3Property, stringIdProperty or groupNameProperty parameter.
These parameters are exposed as function variables and thus can be defined for individual placeholders in the document.

planningsToElements

List<Element> planningsToElements(List<Planning> plannings, Context context);
Maps Planning records to Elements using the standard mapping. The Elements refer to the Bucket entities linked to this Planning record.
If a template is defined for the Planning record, this template is used for the resulting element, otherwise the template defined for the bucket is used.

planningsToElementsGeneric

List<Element> planningsToElementsGeneric(
        List<Planning> inputList,    
        Context context,
        String idProperty,
        String id2Property,
        String id3Property,
        String stringIdProperty,
        String templateIdProperty,
        String classIdProperty,
        String formatStringProperty);
Maps Planning records to Elements using a customized mapping.
All fields of the resulting Elements can be configured either in ISON or as function variables in a generic placeholder. By setting all propertyNames to "> Default property", the same result can be achieved like if using the standard ToElements mapping method.

planningsToProducts

List<Product> planningsToProducts(List<Planning> plannings, Context context);
Maps Planning records to Products using the standard mapping. The Products refer to the Bucket entities linked to this Planning record.
If a template is defined for the Planning record, this template is used for the resulting product, otherwise the template defined for the bucket is used.

planningsToProducts

List<Product> planningsToProducts(
                            List<Planning> plannings, 
                            int mappingFlag, 
                            Context context
                            );
Maps Planning records to Products using the standard mapping. Depending on the mappingFlags, the result products refer to If a template is defined for the Planning record, this template is used for the resulting product, otherwise the template defined for the bucket is used.

Various methods for Planning Data

getPlanningProperties

List<ValueItem> getPlanningProperties(Map<String, Object> parameters);
Returns all properties applicable for planning data plus three values at the top:

Utility methods

  package com.priint.pubserver.comet.bridge.util;
  
  class MappingUtils {

    public static String createBucketIdWithPlanningParent(Bucket bucket, Planning parent);

    public static String createPlanningId(Planning planning, Bucket parent);
  }

Create Comet3 compound IDs from a planning record resp. a bucket with a planning parent.