Chapter 15. Forms in Business Central
A form is a layout definition for a page, defined as HTML, that is displayed as a dialog window to the user during process and task instantiation. Task forms acquire data from a user for both the process and task instance execution, whereas process forms take input and output from process variables.
The input is then mapped to the task using the data input assignment, which you can use inside of a task. When the task is completed, the data is mapped as a data output assignment to provide the data to the parent process instance.
15.1. Form Modeler
Red Hat Process Automation Manager provides a custom editor for defining forms called Form Modeler. With Form Modeler, you can generate forms for data objects, task forms, and process start forms without writing code. Form Modeler includes a widget library for binding multiple data types and a callback mechanism to send notifications when form values change. Form Modeler uses bean-based validation and supports binding form fields to static or dynamic models.
Form Modeler includes the following features:
- Form modeling user interface for forms
- Form auto-generation from the data model or Java objects
- Data binding for Java objects
- Formula and expressions
- Customized forms layouts
- Forms embedding
Form Modeler comes with predefined field types that you place onto the canvas to create a form.
Figure 15.1. Example mortgage loan application form
15.2. Generating process and task forms in Business Central
You can generate a process form from your business process that is displayed at process instantiation to the user who instantiated the process. You can also generate a task form from your business process that is displayed at user task instantiation, when the execution flow reaches the task, to the actor of the user task.
Procedure
-
In Business Central, go to Menu
Design Projects. - Click the project name to open the asset view and then click the business process name.
- In the process designer, click the process task that you want to create a form for (if applicable).
In the upper-right toolbar, click the Form Generation icon and select the forms that you want to generate:
- Generate process form: Generates the form for the entire process. This is the initial form that a user must complete when the process instance is started.
- Generate all forms: Generates the form for the entire process and for all user tasks.
- Generate forms for selection: Generates the forms for the selected user task nodes.
Figure 15.2. Form generation menu
The forms are created in the root directory of your project.
- Go to the root directory of your project in Business Central, click the new form name, and use the Form Modeler to customize the form to meet your requirements.
15.3. Manually creating forms in Business Central
You can create task and process forms manually from your project asset view. This is another way to generate a form without selecting to generate forms from your business process. For example, the Form Modeler now supports creating forms from external data objects.
Procedure
-
In Business Central, go to Menu
Design Projects and click the project name. -
Click Add Asset
Form. Provide the following information in the Create new Form window:
- Form name (must be unique)
- Package name
Model type: Select either Business Process or Data Object.
- For the Business Process model type, select your business process from the Select Process drop-down menu, and then select the form that you want to create from the Select Form drop-down menu.
- For the Data Object model type, select one of your project data objects from the Select Data Object from Project drop-down menu.
- Click Ok to open the Form Modeler.
- In the Components view on the left side of the Form Modeler, expand the Model Fields and Form Controls menus and create a new form by dragging your required fields and form controls to the canvas.
- Click Save to save your changes.
15.4. Document attachments in a form or process
Red Hat Process Automation Manager supports document attachments in forms using the Document
form field. With the Document
form field, you can upload documents that are required as part of a form or process.
To enable document attachments in forms and processes, complete the following procedures:
- Set the document marshalling strategy.
- Create a document variable in the business process.
- Map the task inputs and outputs to the document variable.
15.4.1. Setting the document marshalling strategy
The document marshalling strategy for your project determines where documents are stored for use with forms and processes. The default document marshalling strategy in Red Hat Process Automation Manager is org.jbpm.document.marshalling.DocumentMarshallingStrategy
. This strategy uses a DocumentStorageServiceImpl
class that stores documents locally in your PROJECT_HOME/.docs
folder. You can set this document marshalling strategy or a custom document marshalling strategy for your project in Business Central or in the kie-deployment-descriptor.xml
file.
Procedure
-
In Business Central, go to Menu
Design Projects. - Select a project. The project Assets window opens.
Click the Settings tab.
Figure 15.3. Settings tab
-
Click Deployments
Marshalling Strategies→ Add Marshalling Strategy. In the Name field, enter the identifier of a document marshalling strategy, and in the Resolver drop-down menu, select the corresponding resolver type:
-
For single documents: Enter
org.jbpm.document.marshalling.DocumentMarshallingStrategy
as the document marshalling strategy and set the resolver type to Reflection. -
For multiple documents: Enter
new org.jbpm.document.marshalling.DocumentCollectionImplMarshallingStrategy(new org.jbpm.document.marshalling.DocumentMarshallingStrategy())
as the document marshalling strategy and set the resolver type to MVEL. - For custom document support: Enter the identifier of the custom document marshalling strategy and select the relevant resolver type.
-
For single documents: Enter
- Click Test to validate your deployment descriptor file.
Click Deploy to build and deploy the updated project.
Alternatively, if you are not using Business Central, you can navigate to
PROJECT_HOME/src/main/resources/META_INF/kie-deployment-descriptor.xml
(if applicable) and edit the deployment descriptor file with the required<marshalling-strategies>
elements.- Click Save.
Example deployment descriptor file with document marshalling strategy for multiple documents
<deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit>org.jbpm.domain</persistence-unit> <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit> <audit-mode>JPA</audit-mode> <persistence-mode>JPA</persistence-mode> <runtime-strategy>SINGLETON</runtime-strategy> <marshalling-strategies> <marshalling-strategy> <resolver>mvel</resolver> <identifier>new org.jbpm.document.marshalling.DocumentCollectionImplMarshallingStrategy(new org.jbpm.document.marshalling.DocumentMarshallingStrategy());</identifier> </marshalling-strategy> </marshalling-strategies>
15.4.1.1. Using a custom document marshalling strategy for a content management system (CMS)
The document marshalling strategy for your project determines where documents are stored for use with forms and processes. The default document marshalling strategy in Red Hat Process Automation Manager is org.jbpm.document.marshalling.DocumentMarshallingStrategy
. This strategy uses a DocumentStorageServiceImpl
class that stores documents locally in your PROJECT_HOME/.docs
folder. If you want to store form and process documents in a custom location, such as in a centralized content management system (CMS), add a custom document marshalling strategy to your project. You can set this document marshalling strategy in Business Central or in the kie-deployment-descriptor.xml
file directly.
Procedure
Create a custom marshalling strategy
.java
file that includes an implementation of theorg.kie.api.marshalling.ObjectMarshallingStrategy
interface. This interface enables you to implement the variable persistence required for your custom document marshalling strategy.The following methods in this interface help you create your strategy:
-
boolean accept(Object object)
: Determines if the specified object can be marshalled by the strategy -
byte[] marshal(Context context, ObjectOutputStream os, Object object)
: Marshals the specified object and returns the marshalled object asbyte[]
-
Object unmarshal(Context context, ObjectInputStream is, byte[] object, ClassLoader classloader)
: Reads the object received asbyte[]
and returns the unmarshalled object -
void write(ObjectOutputStream os, Object object)
: Same as themarshal
method, provided for backward compatibility -
Object read(ObjectInputStream os)
: Same as theunmarshal
method, provided for backward compatibility
The following code sample is an example
ObjectMarshallingStrategy
implementation for storing and retrieving data from a Content Management Interoperability Services (CMIS) system:Example implementation for storing and retrieving data from a CMIS system
package org.jbpm.integration.cmis.impl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import org.apache.chemistry.opencmis.client.api.Folder; import org.apache.chemistry.opencmis.client.api.Session; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.commons.io.IOUtils; import org.drools.core.common.DroolsObjectInputStream; import org.jbpm.document.Document; import org.jbpm.integration.cmis.UpdateMode; import org.kie.api.marshalling.ObjectMarshallingStrategy; public class OpenCMISPlaceholderResolverStrategy extends OpenCMISSupport implements ObjectMarshallingStrategy { private String user; private String password; private String url; private String repository; private String contentUrl; private UpdateMode mode = UpdateMode.OVERRIDE; public OpenCMISPlaceholderResolverStrategy(String user, String password, String url, String repository) { this.user = user; this.password = password; this.url = url; this.repository = repository; } public OpenCMISPlaceholderResolverStrategy(String user, String password, String url, String repository, UpdateMode mode) { this.user = user; this.password = password; this.url = url; this.repository = repository; this.mode = mode; } public OpenCMISPlaceholderResolverStrategy(String user, String password, String url, String repository, String contentUrl) { this.user = user; this.password = password; this.url = url; this.repository = repository; this.contentUrl = contentUrl; } public OpenCMISPlaceholderResolverStrategy(String user, String password, String url, String repository, String contentUrl, UpdateMode mode) { this.user = user; this.password = password; this.url = url; this.repository = repository; this.contentUrl = contentUrl; this.mode = mode; } public boolean accept(Object object) { if (object instanceof Document) { return true; } return false; } public byte[] marshal(Context context, ObjectOutputStream os, Object object) throws IOException { Document document = (Document) object; Session session = getRepositorySession(user, password, url, repository); try { if (document.getContent() != null) { String type = getType(document); if (document.getIdentifier() == null || document.getIdentifier().isEmpty()) { String location = getLocation(document); Folder parent = findFolderForPath(session, location); if (parent == null) { parent = createFolder(session, null, location); } org.apache.chemistry.opencmis.client.api.Document doc = createDocument(session, parent, document.getName(), type, document.getContent()); document.setIdentifier(doc.getId()); document.addAttribute("updated", "true"); } else { if (document.getContent() != null && "true".equals(document.getAttribute("updated"))) { org.apache.chemistry.opencmis.client.api.Document doc = updateDocument(session, document.getIdentifier(), type, document.getContent(), mode); document.setIdentifier(doc.getId()); document.addAttribute("updated", "false"); } } } ByteArrayOutputStream buff = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream( buff ); oos.writeUTF(document.getIdentifier()); oos.writeUTF(object.getClass().getCanonicalName()); oos.close(); return buff.toByteArray(); } finally { session.clear(); } } public Object unmarshal(Context context, ObjectInputStream ois, byte[] object, ClassLoader classloader) throws IOException, ClassNotFoundException { DroolsObjectInputStream is = new DroolsObjectInputStream( new ByteArrayInputStream( object ), classloader ); String objectId = is.readUTF(); String canonicalName = is.readUTF(); Session session = getRepositorySession(user, password, url, repository); try { org.apache.chemistry.opencmis.client.api.Document doc = (org.apache.chemistry.opencmis.client.api.Document) findObjectForId(session, objectId); Document document = (Document) Class.forName(canonicalName).newInstance(); document.setAttributes(new HashMap<String, String>()); document.setIdentifier(objectId); document.setName(doc.getName()); document.setLastModified(doc.getLastModificationDate().getTime()); document.setSize(doc.getContentStreamLength()); document.addAttribute("location", getFolderName(doc.getParents()) + getPathAsString(doc.getPaths())); if (doc.getContentStream() != null && contentUrl == null) { ContentStream stream = doc.getContentStream(); document.setContent(IOUtils.toByteArray(stream.getStream())); document.addAttribute("updated", "false"); document.addAttribute("type", stream.getMimeType()); } else { document.setLink(contentUrl + document.getIdentifier()); } return document; } catch(Exception e) { throw new RuntimeException("Cannot read document from CMIS", e); } finally { is.close(); session.clear(); } } public Context createContext() { return null; } // For backward compatibility with previous serialization mechanism public void write(ObjectOutputStream os, Object object) throws IOException { Document document = (Document) object; Session session = getRepositorySession(user, password, url, repository); try { if (document.getContent() != null) { String type = document.getAttribute("type"); if (document.getIdentifier() == null) { String location = document.getAttribute("location"); Folder parent = findFolderForPath(session, location); if (parent == null) { parent = createFolder(session, null, location); } org.apache.chemistry.opencmis.client.api.Document doc = createDocument(session, parent, document.getName(), type, document.getContent()); document.setIdentifier(doc.getId()); document.addAttribute("updated", "false"); } else { if (document.getContent() != null && "true".equals(document.getAttribute("updated"))) { org.apache.chemistry.opencmis.client.api.Document doc = updateDocument(session, document.getIdentifier(), type, document.getContent(), mode); document.setIdentifier(doc.getId()); document.addAttribute("updated", "false"); } } } ByteArrayOutputStream buff = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream( buff ); oos.writeUTF(document.getIdentifier()); oos.writeUTF(object.getClass().getCanonicalName()); oos.close(); } finally { session.clear(); } } public Object read(ObjectInputStream os) throws IOException, ClassNotFoundException { String objectId = os.readUTF(); String canonicalName = os.readUTF(); Session session = getRepositorySession(user, password, url, repository); try { org.apache.chemistry.opencmis.client.api.Document doc = (org.apache.chemistry.opencmis.client.api.Document) findObjectForId(session, objectId); Document document = (Document) Class.forName(canonicalName).newInstance(); document.setIdentifier(objectId); document.setName(doc.getName()); document.addAttribute("location", getFolderName(doc.getParents()) + getPathAsString(doc.getPaths())); if (doc.getContentStream() != null) { ContentStream stream = doc.getContentStream(); document.setContent(IOUtils.toByteArray(stream.getStream())); document.addAttribute("updated", "false"); document.addAttribute("type", stream.getMimeType()); } return document; } catch(Exception e) { throw new RuntimeException("Cannot read document from CMIS", e); } finally { session.clear(); } } }
-
-
In Business Central, go to Menu
Design Projects. Click the project name and click Settings.
Figure 15.4. Settings tab
-
Click Deployments
Marshalling Strategies→ Add Marshalling Strategy. -
In the Name field, enter the identifier of the custom document marshalling strategy, such as
org.jbpm.integration.cmis.impl.OpenCMISPlaceholderResolverStrategy
in this example. - Select the relevant option from the Resolver drop-down menu, such as Reflection in this example.
- Click Test to validate your deployment descriptor file.
Click Deploy to build and deploy the updated project.
Alternatively, if you are not using Business Central, you can navigate to
PROJECT_HOME/src/main/resources/META_INF/kie-deployment-descriptor.xml
(if applicable) and edit the deployment descriptor file with the required<marshalling-strategies>
elements.Example deployment descriptor file with custom document marshalling strategy
<deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit>org.jbpm.domain</persistence-unit> <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit> <audit-mode>JPA</audit-mode> <persistence-mode>JPA</persistence-mode> <runtime-strategy>SINGLETON</runtime-strategy> <marshalling-strategies> <marshalling-strategy> <resolver>reflection</resolver> <identifier> org.jbpm.integration.cmis.impl.OpenCMISPlaceholderResolverStrategy </identifier> </marshalling-strategy> </marshalling-strategies>
- To enable documents stored in a custom location to be attached to forms and processes, create a document variable in the relevant processes and map task inputs and outputs to that document variable in Business Central.
15.4.2. Creating a document variable in a business process
After you set a document marshalling strategy, create a document variable in the related process to upload documents to a human task and for the document or documents to be visible in the Process Instances view in Business Central.
Prerequisites
- You have set a document marshalling strategy as described in Section 15.4.1, “Setting the document marshalling strategy”.
Procedure
-
In Business Central, go to Menu
Design Projects. - Click the project name to open the asset view and click the business process name.
- Click the canvas and click on the right side of the window to open the Properties panel.
Expand Process Data and click and enter the following values:
-
Name:
document
-
Custom Type:
org.jbpm.document.Document
for a single document ororg.jbpm.document.DocumentCollection
for multiple documents
-
Name:
15.4.3. Mapping task inputs and outputs to the document variable
If you want to view or modify the attachments inside of task forms, create assignments inside of the task inputs and outputs.
Prerequisites
- You have a project that contains a business process asset that has at least one user task.
Procedure
-
In Business Central, go to Menu
Design Projects. - Click the project name to open the asset view and click the business process name.
- Click a user task and click on the right side of the window to open the Properties panel.
- Expand Implementation/Execution and next to Assignments, click to open the Data I/O window.
Next to Data Inputs and Assignments, click Add and enter the following values:
-
Name:
taskdoc_in
-
Data Type:
org.jbpm.document.Document
for a single document ororg.jbpm.document.DocumentCollection
for multiple documents -
Source:
document
-
Name:
Next to Data Outputs and Assignments, click Add and enter the following values:
-
Name:
taskdoc_out
-
Data Type:
org.jbpm.document.Document
for a single document ororg.jbpm.document.DocumentCollection
for multiple documents -
Target:
document
The
Source
andTarget
fields contain the name of the process variable you created earlier.-
Name:
- Click Save.