Inserting user activities
In this topic, you will find basic information on the use and customization of the BPMN item User Task (user activity).
Useful information on user activities
The BPMN standard includes different constructs for assigning recipients within a User Task (user activity).
We recommend using the property camunda:candidateUsers.
Potential Owner
<userTask id='theTask' name='important task' camunda:candidateUsers="${variables.get('myPerformer')}" />
Using constants for recipients
If you want to state the recipient of an activity as a constant, use the following syntax:
${collections.from("identity:///identityprovider/scim/users/someUserId")} // or ${collections.from("identity:///identityprovider/scim/users/someUserId", "identity:///identityprovider/scim/users/someOtherUserId")}
Using variables for recipients
If the recipients of an activity are to determined from variables, use the following syntax to reference the variables:
${variables.get("variableName")}
Linking additional information to user activities
To add additional information to user activities for processing, you can use the process variable dv_attachment reserved for this purpose.
You can save one URL in this variable. The URL is added during the creation of an activity if you have entered it beforehand.
Warning
The process variable dv_attachment is predefined with the data type DmsObject . If you want to use a different URI than that for an object of the DMS app, you need to change the value of the data type to String.
Adding metadata to user activities
You can define metadata within the BPMN item userTask in order to add this metadata to user activities. Add the item extensionElements and, under it, the item camunda:properties if the process definition does not contain these items yet. You can now define metadata as follows below the item camunda:properties:
<userTask> ... <extensionElements> <camunda:properties> <camunda:property name="metadata:invoiceNumber:value" value="${variables.get("invoiceNumber")}" /> <camunda:property name="metadata:invoiceNumber:caption" value="Invoice Number" /> <camunda:property name="metadata:invoiceNumber:caption:de" value="Rechnungsnummer" /> <camunda:property name="metadata:invoiceNumber:type" value="String" /> </camunda:properties> </extensionElements> ... </userTask>
The property name within the item camunda:property can accept the following values:
Value | Meaning | |
---|---|---|
metadata:<key>:value | Defines the value of a metadata item. | The key of the metadata item must correspond to the following expression: [A-Za-z0-9]{1,255} |
metadata:<key>:caption | Defines the standard designation of a metadata item (optional). If it does not exist, the key is used as the designation. | |
metadata:<key>:caption:<language> | Defines the designation of a metadata item for the language specified (optional). The language code must consist of two letters pursuant to the ISO 639-1 standard. If it does not exist, the standard designation or key is used. | |
metadata:<key>:type | (optional) defines the data type of the meta data | Equals the TaskApp data type (String, Number, Money, Date). If no type is specified, the type String is assumed. |
The property value can contain either a fixed value or an expression. This value can be a maximum of 255 characters long. When an expression is used, this restriction applies to the result of the expression. In addition, the result of an expression must be an individual value. Values of the type Collection or Array are not permitted.
Determining the storage duration of a user activity
You can define the storage duration of user activities within the BPMN item userTask. Add the item extensionElements and, under it, the item camunda:properties if the process definition does not contain these items yet. You can now define the storage duration as follows under the item camunda:properties.
<userTask> ... <extensionElements> <camunda:properties> <camunda:property name="retentionTime" value="P7D" /> </camunda:properties> </extensionElements> ... </userTask>
The property value can contain either a fixed value or an expression. Define the storage duration as a period of time in days pursuant to ISO 8601, for example, P30D for 30 days.
Determining the usage of actions in the user interface of a user activity
You can define the usage of actions in the user interface of a user activity within the BPMN item userTask. Add the item extensionElements and, under it, the item camunda:properties if the process definition does not contain these items yet. You can now define the storage duration as follows under the item camunda:properties.
<userTask> ... <extensionElements> <camunda:properties> <camunda:property name="actionScope:complete" value="[list, details]" /> </camunda:properties> </extensionElements> ... </userTask>
The property name of the element contains the prefix actionScope followed by the action which should be configured (in this example complete). The property value contains the options for the usage. For information on possible actions and usage options, see the API documentation of the task app.
Adding user interfaces to a user activity
When you use the BPMN item User Task (user activity) for the creation of a process model, you can add a user interface to the activity. The user will then e.g. be shown a button which he/she can use to start a task.
To add a user interface, use the property formKey.
Example
Form Key
<userTask id="someTask" camunda:formKey="uri:/myapp/myform"> ... </userTask>
The key consists of a prefix and a value. These are separated by a colon. The following prefixes can be used:
Prefix | Meaning | Example |
---|---|---|
uri | The user interface is indicated using a URI. | uri:https://example.com/ |
Using process variables
The key can also contain process variables. These can be indicated in the form of Expression Language.
Example
uri:${variables.get("formUri")} uri:https://example.com/?queryParam=${variables.get("formParam")}
External access to process variables
You can use the expression ${process.task.variablesUri} to generate a URI in order to permit an external application to access the current variables of this user interface. You can use the HTTP methods GET and PUT to read and change these variables. In this user interface, an input and output mapping needs to be defined for the variables used.
Example
uri:/myApp?variablesUri=${process.task.variablesUri}
Using process variables in a user interface
If a user activity contains a form, you have read and write access via HTTP to the process variables used.
When getting started with process variables in a form, it is useful to create a simple process with one user activity first. This process will e.g. look like this:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" targetNamespace="" exporter="d.velop process modeler"> <process id="GettingStarted" name="Example: Getting started" isExecutable="true"> <startEvent id="start"/> <userTask id='task_hello_world' name='Hello World' camunda:candidateUsers="${variables.get('dv_initiator')}" /> <endEvent id="end"/> <sequenceFlow id="s1" sourceRef="start" targetRef="task_hello_world" /> <sequenceFlow id="s2" sourceRef="task_hello_world" targetRef="end" /> </process> </definitions>
To simplify the example, the graphic information on the BPMN diagram is not included in this BPMN sample file.
Creating a form
First create an HTML file. This may look as follows. The code examples are based on the functionality in Google Chrome. Other browsers may require different syntax.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Getting started</title> </head> <body/> </html>
Store the HTML file so that it is accessible by an HTTP gateway. Add the URI of the HTML document to the BPMN item userTask .
... <process id="GettingStarted" name="Example: Getting started" isExecutable="true"> ... <userTask id='task_hello_world' name='Hello World' camunda:formKey="uri:/res/process/process-form.html?variables=${process.task.variablesUri}" camunda:candidateUsers="${variables.get('dv_initiator')}" /> ...
Explanation of the property
camunda:formKey: This property defines the URI which deploys the user interface for the user activity. The prefix uri: is mandatory here. The variable ${process.task.variablesUri} is appended to the URI as a query parameter. The variable is resolved by the application during runtime into a URI which deploys an HTTP endpoint for reading and writing of the variables.
Creating the user interface
First create the basic structure for an HTML table in your HTML document. Over the further course, the variables will then be displayed in this table.
... <body> <table id="variables"> <thead> <tr> <th>Name</th> <th>Value</th> </tr> </thead> <tbody id="variableValues"> </tbody> </table> </body> ...
Querying the process variables
In the head of the HTML document, you create a function for reading the process variables. The process variables are queried by the application using an HTTP GET request according to the REST API. Loading the variables is initiated by the event onLoad of the item body.
... <head> ... <script type="text/javascript"> var urlParams = new URLSearchParams(window.location.search); var variablesUri = urlParams.get('variables'); function loadVariables() { fetch(variablesUri) .then(response => response.json()); } </script> </head> <body onload="loadVariables()"> ...
After the variables have been loaded, you can now view them in the prepared table. The following example uses the JavaScript library jQuery. Add the library jQuery in the head of the HTML document and implement suitable methods to view the variables.
... <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> <script type="text/javascript"> var urlParams = new URLSearchParams(window.location.search); var variablesUri = urlParams.get('variables'); function loadVariables() { fetch(variablesUri) .then(response => response.json()) .then(json => createTableFromVariables(json.variables || {})); } function createTableFromVariables(variables) { Object.keys(variables).forEach(variable => { var value = variables[variable]; $('#variableValues').append( `<tr class="variable">${createKeyColumn(variable)}${createValueColumn(value)}</tr>`); }); } function createKeyColumn(variable) { return `<td><span class="key">${variable}</span></td>`; } function createValueColumn(value) { return `<td><input class="value" type="text" value="${value}"></td>`; } </script> </head> <body onload="loadVariables()"> ...
The variables are now queried and displayed after the HTML document has been loaded.
Writing the process variables
Add methods to the script section which enable writing the variables back into the application. Then add a button below the table which calls the method saveVariables.
... <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> <script type="text/javascript"> var urlParams = new URLSearchParams(window.location.search); var variablesUri = urlParams.get('variables'); ... function saveVariables() { var variables = createVariablesFromTable(); // These variables are read-only and must not be sent to server when setting variables delete variables['dv_initiator']; delete variables['dv_start_time']; const headers = new Headers(); headers.append('Cache-Control', 'no-cache'); headers.append('Content-Type', 'application/json'); let promise = fetch(variablesUri, { method: 'PUT', headers: headers, credentials: 'same-origin', body: JSON.stringify({variables}) }); promise.then(function(response) { let status = response.status; if (status !== 200 ) { window.alert("Saving variables failed: " + status); } else { window.alert("Variables saved"); } }); } function createVariablesFromTable() { var variables = {}; $("tr.variable").each(function() { $this = $(this); var key = $this.find("span.key").html(); var value = $this.find("input.value").val(); variables[key] = value; }); return variables; } </script> </head> <body onload="loadVariables()"> ... <br/> <button onClick="saveVariables()">Save</button> </body>
You can use the form displayed on execution of the process to view and change the current process variables.
The final HTML document will look as follows. The HTML document contains several complementary CSS definitions for optimized display of the user interface items.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Process Form</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> <style> table { border-collapse: collapse; width: 100%; } table, th, td { border: 1px solid black; } td { padding: 3px; } .key { color: gray; } th { text-align: left; } input { width: 100%; border: 0; } </style> <script type="text/javascript"> var urlParams = new URLSearchParams(window.location.search); var variablesUri = urlParams.get('variables'); function loadVariables() { fetch(variablesUri) .then(response => response.json()) .then(json => createTableFromVariables(json.variables || {})); } function saveVariables() { var variables = createVariablesFromTable(); // These variables are read-only and must not be sent to server when setting variables delete variables['dv_initiator']; delete variables['dv_start_time']; const headers = new Headers(); headers.append('Cache-Control', 'no-cache'); headers.append('Content-Type', 'application/json'); let promise = fetch(variablesUri, { method: 'PUT', headers: headers, credentials: 'same-origin', body: JSON.stringify({variables}) }); promise.then(function(response) { let status = response.status; if (status !== 200 ) { window.alert("Saving variables failed: " + status); } else { window.alert("Variables saved"); } }); } function createTableFromVariables(variables) { Object.keys(variables).forEach(variable => { var value = variables[variable]; $('#variableValues').append( `<tr class="variable">${createKeyColumn(variable)}${createValueColumn(value)}</tr>`); }); } function createKeyColumn(variable) { return `<td><span class="key">${variable}</span></td>`; } function createValueColumn(value) { return `<td><input class="value" type="text" value="${value}"></td>`; } function createVariablesFromTable() { var variables = {}; $("tr.variable").each(function() { $this = $(this); var key = $this.find("span.key").html(); var value = $this.find("input.value").val(); variables[key] = value; }); return variables; } </script> </head> <body onload="loadVariables()"> <table id="variables"> <thead> <tr> <th>Name</th> <th>Value</th> </tr> </thead> <tbody id="variableValues"> </tbody> </table> <br/> <button onClick="saveVariables()">Save</button> </body> </html>