FreemarkerForms
Using task forms
RunaWFE Free Workflow System (BPMS) Version 4.5.0
© 2003 - 2015, Consulting Group Runa
© 2015 - 2024, "Process Technologies" Ltd, this document is available under GNU FDL license. RunaWFE Free is an open source system distributed under a LGPL license (http://www.gnu.org/licenses/lgpl.html).
# Introduction
A form contains freemarker components and html tags. Any freemarker code can be used, but graphical forms editor (based on build-in browser) may remove them. Freemarker extention in the system are the components which are freemarker methods.
Components development (adding new components) see in Developer Studio Developer Guide and WFE Server Developer Guide.
Note. If variable name contains symbols that are not allowed to be used in JavaScript or CSS, it's possible to use variable scripting name that doesn't contain such symbols.
# JavaScript
Every form in Developer Studio can be connected to a JavaScript script that runs when form is displayed.
# External JS libraries
Using custom form component
This method is used in ru.runa.wf.web.ftl.method.TreeviewSupportTag during html generation:
<link rel='stylesheet' href='" + webHelper.getUrl("/css/jquery.treeview.css") + "'> <script type='text/javascript' src='" + webHelper.getUrl("/js/jquery.treeview.js") + "'>c=0;</script>
Using setting 4.2.0+
In web.properties there is available option ServerConfigurationGuide#task.form.external.js.libs.
Sample:
Content of wfe.custom.web.properties:
task.form.external.js.libs = http://yastatic.net/raphael/2.1.0/raphael.min.js
Business process File:Raphael form test.par
# Hidding or changing task submit button
Some asynchronous tasks are not supposed to be performed by user manually (for example monitoring process progress task). Submit button can be hidden for such tasks with the help of script:
$(document).ready(function() { $("input[name='submitButton']").hide(); });
The following script disables the button and changes it's name:
$(document).ready(function() { $("input[name='submitButton']").val("This task should not be completed manually"); $("input[name='submitButton']").attr("disabled", "disabled"); });
Process example: File:HideSubmitButtonSample.par
# Configuring text display
Text type variable component generates textarea with disabled attribute. No additional style can be added to it in IE, no scrolling or color change is available.
It was possible to generate textarea with readonly attribute instead, but we decided against it because: а) textarea for input and readonly textarea would have looked the same and it might confuse user. б) it would stand out from the rest of the output elements
Still it's possible to change it with the scripts
- to replace disabled to readonly for textarea:
$(document).ready(function() { $("textarea[name='VARIABLE_NAME']").removeAttr("disabled"); $("textarea[name='VARIABLE_NAME']").attr("readonly", "true"); });
- to add readonly to textarea:
$(document).ready(function() { $("textarea[name='VARIABLE_NAME']").attr("readonly", "true"); });
# CSS
Each process can be link to cascade style sheet. It can be done in Developer Studio from popup menu (click on the blank space of the process graph for it to appear).
CSS rules are applied to the whole process, but it's possible to create selectors in a way that it is different for each single element, for example:
- a rule for all textarea elements
.inputText { background-color: gray; }
- a rule for all select elements on form with ID = ID23
.ID23 select { padding: 10px; }
- a rule for all text elements on form with ID = ID23
.ID23 .inputText { background-color: gray; }
- a rule to display variable "Variable2" as text on form with ID = ID23
.ID23 .Variable2 { border: 1px solid brown; display: block; padding: 10px; }
# List type
Input and output of list type elements in HTML can be done using element index. For example, for Variable2 list variable the elements in HTML can be displayed as Variable2[0], Variable2[1], Variable2[2], ...
# Executor type variable output
Executor type variable output is carried out as usual in freemarker syntax. For example, for full name output of processStarter of type actor:
${processStarter.fullName} ${processStarter.active?string("yes", "no")}
Variables with space (and other special) symbols in their name can be used with their script name
${process_starter.fullName} (for process started variable)
# Context variables 4.2.0+
In forms there are available
User interaction form (${context.interaction}, ru.runa.wfe.form.Interaction)
Use java script validation ${context.interaction.useJSValidation?string("yes", "no")} Required fields list ${context.interaction.requiredVariableNames}
Task not for the start form (${context.task}, ru.runa.wfe.task.dto.WfTask)
ID ${context.task.id} Name ${context.task.name} Description ${context.task.description} Role name ${context.task.swimlaneName} Executor ${context.task.owner.label} Creation date ${context.task.creationDate?datetime} Acquired by escalation rules? ${context.task.escalated?string("yes", "no")} Acquired by substitution rules? ${context.task.acquiredBySubstitution?string("yes", "no")}
Process definition (${context.definition}, ru.runa.wfe.definition.dto.WfDefinition)
ID ${context.definition.id} Name ${context.definition.name} Description ${context.definition.description} Version ${context.definition.version} Deployed ${context.definition.deployedDate?datetime}
Process not for the start form (${context.process}, ru.runa.wfe.execution.dto.WfProcess)
ID ${context.process.id} Start date ${context.process.startDate?datetime} End date ${context.process.endDate?datetime} (for async tasks only) Subprocess IDs ${context.process.hierarchyIds}
# Active components (not deprecated)
# InputVariable
It is used on form for variable input. The display of the component differs depending on variable type
first parameter | Name of variable of any type. The input value is saved into this variable |
Simple types
variable type | output |
BooleanFormat | true or false |
LongFormat (integer), DoubleFormat (decimal), BigDecimalFormat | Number as a string |
DateFormat | Date as a string |
DateTimeFormat | Date with time as a string |
TimeFormat | Time as a string |
StringFormat | String |
TextFormat | Text as a string |
FileFormat | Link to download the file |
ExecutorFormat, GroupFormat, ActorFormat | Link to executor if read permission present, otherwise executor full name |
List
List input is done via EditList component. It generates JavaScript to add and remove elements of the list. Element type information is used to generate corresponding (see table above) input element.
Note. Currently Map input is not supported.
# Example of Autocomplete
Variable month input:
${InputVariable("month")}
In the form script use:
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; $(document).ready(function() { $("input[name='month']").autocomplete( { delay: 300, minLength: 0, source: monthNames }); $("input[name='month']").focus(function() { $(this).autocomplete("search", $(this).val()); }); });
# Example of Autocomplete with loading from database 4.1.2+
For this demonstration the following table is used:
CREATE TABLE apples (id bigint NOT NULL, label character varying(255)) INSERT INTO apples(id, label) VALUES (301, 'Gala'); INSERT INTO apples(id, label) VALUES (302, 'Red Delicious'); INSERT INTO apples(id, label) VALUES (303, 'Granny Smith'); INSERT INTO apples(id, label) VALUES (304, 'Fuji'); INSERT INTO apples(id, label) VALUES (305, 'Golden delicious');
WFE Server has a configured datasource with JNDI name postgres-ds and extended application context (wfe.custom.system.context.xml), in which new command demoListView is registered:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <bean id="demoDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:postgres-ds"/> </bean> <bean id="demoListView" class="ru.runa.wfe.commons.web.SQLListViewCommand"> <property name="dataSource" ref="demoDataSource" /> <property name="tableName" value="apples" /> <property name="valueColumnName" value="id" /> <property name="labelColumnName" value="label" /> </bean> </beans>
In form we input variables selectedValue, selectedText.
In form script we use our command:
$(document).ready(function() { $("input[name='selectedText']").attr("readonly", "true"); $("input[name='selectedText']").css("cursor", "pointer"); $("input[name='selectedText']").autocomplete({ delay: 300, minLength: 0, source: function(request, response) { $.getJSON("/wfe/ajaxcmd?command=demoListView", { filter: request.term }, response); }, select: function(event, ui) { $("input[name='selectedValue']").val(ui.item.value); $("input[name='selectedText']").val(ui.item.label); return false; } }); $("input[name='selectedText']").focus(function() { $(this).autocomplete("search", $(this).val()); }); });
Sample process File:ListDemo.par
# DisplayVariable
The component displays variable depending on variable type.
first parameter | Name of a variable of any type to display a value from |
second parameter | Display mode: false = as a string, true = as disabled input element |
Display mode: as a string
Output in this mode depends on variable type.
Simple types
variable type | output |
BooleanFormat | true or false |
LongFormat, DoubleFormat, BigDecimalFormat | A number as a string |
DateFormat | Date as a string |
DateTimeFormat | Date with time as a string |
TimeFormat | Time as a string |
StringFormat | String |
TextFormat | Text as a string |
FileFormat | Link to download |
ExecutorFormat, GroupFormat, ActorFormat | A link to executor in the system if permissions allow or just full name if not. |
List
A list of values [element1, element2, ...], with elements formatted depending on element type.
Note. Currently Map output is not supported.
# Hints for executor types 4.1.2+
You can enable tooltip for links for these types in task forms. Create file ${type}.tooltip.template and place it to the application classpath.
variable type | tooltip template file name |
executor | ru.runa.wfe.user.Executor.tooltip.template |
user | ru.runa.wfe.user.Actor.tooltip.template |
group | ru.runa.wfe.user.Group.tooltip.template |
In these files allowed freemarker syntax with HTML using executor attributes.
Example for variable of type user:
<#if object.description?? && object.description?length != 0> Description: <b>${object.description}<br> </#if> <#if object.phone?? && object.phone?length != 0> Phone: <b>${object.phone}<br> </#if> <#if object.email?? && object.email?length != 0> Email: <b>${object.email} </#if>
Display mode: disabled input elements
It is similar to input component but the component is disabled.
# EditLinkedLists
${EditLinkedLists("true", "true", "true", "Variable1", "Variable2")}
The first 3 parameters are fixed:
1st parameter | Allow to add elements in the lists |
2nd parameter | Allow to edit existing elements in the lists |
3rd parameter | Allow to remove existing elements from the lists |
The following parameters indicate which lists are edited.
It's displayed as a table with indicated lists as columns (elements input is similar to variable input and depend on element type).
Say list variable name is "list1". If there's a string variable "list1_header" in the process then while displaying list1 as a column of table value of list1_header is used for this column header.
The component triggers onRowAdded, onRowRemoved events on the root element <TABLE id="editLinkedLists"> wnen rows on form are edited or removed correspondingly.
Example of script which prohibits to edit certain column
$(document).ready(function() { $("textarea[name='author_texts']").attr("readonly", "true"); $("input[name='athors']").attr("readonly", "true"); });
# DisplayLinkedLists
${DisplayLinkedLists("Variable1", "Variable2", "Variable3", "Variable4")}
There can be any number of lists given as component parameters.
It is displayed as a table with lists as its columns (elements are displayed depending on its type). Lists can be of any size.
Say list variable name is "list1". If there's a string variable "list1_header" in the process then while displaying list1 as a column of table value of list1_header is used for this column header.
Example:
<TABLE class="displayLinkedLists" rowscount="1"> <TR class="header"> <TD><TD><B>date and time of leaving</B></TD><TD><B>contacts</B></TD><TD><B>coming in time</B></TD><TD><B>leaving time</B></TD> </TR> <TR row="0"> <TD column="0"> <INPUT class="inputDateTime" disabled="true" name="list of date and time of leaving[0]" style="width: 150px;" type="text" value="11.08.2013 10:00"> </TD> <TD column="1"> <INPUT class="inputString" disabled="true" name="contacts[0]" type="text" value="139129431"> </TD> <TD column="2"> <INPUT class="inputTime" disabled="true" name="coming in time[0]" style="width: 50px;" type="text"> </TD> <TD column="3"> <INPUT class="inputTime" disabled="true" name="leaving time[0]" style="width: 50px;" type="text"> </TD> </TR> </TABLE>
Example of script that colors the table above
$(document).ready(function() { var rows = $(".displayLinkedLists").attr("rowsCount"); for (var row=0; row<rows; row++) { var inTime = $("input[name='coming in time["+row+"]']").val(); var outTime = $("input[name='leaving time["+row+"]']").val(); if (inTime != "" && outTime == "") { $(".displayLinkedLists tr[row='"+row+"']").css("background-color", "#ff0000"); } else if (inTime == "" && outTime == "") { $(".displayLinkedLists tr[row='"+row+"']").css("background-color", "#ffff77"); } } });
# DisplayLinkedMaps
${DisplayLinkedMaps("Variable1", "Variable2")}
Component has as many parameters as many maps are displayed.
Maps elements values are displayed in table columns (element value type determines the way it's displayed). Maps can be of any size. Each table row contains map values that corresponds to the same key.
Say map variable name is "map1". If there's a string variable "map1_header" in the process then while displaying map1 elements values as a column of table value of map1_header is used for this column header.
# ChooseExecutorFromRelation
This component displays a list for executor choice from one side of a relation.
1st parameter | Executor variable where the choice result is saved |
2nd parameter | Relation name with value@ prefix or variable that contains relation name as its value |
3rd parameter | Inverse relation. If true a list of executors from right side of relation (if false then from left side) is displayed. |
# ChooseExecutorByRelation
This component displays a filtered by parameter list for executor choice from one side of a relation.
1st parameter | Executor variable where the choice result is saved |
2nd parameter | Relation name with value@ prefix or variable that contains relation name as its value
|
3rd parameter | Relation parameter to filter pairs either by right side or by left side depending on the 4th parameter. A variable of executor or string type (with group or actor name with value@ prefix) can be used. Executor and all groups that includes executor are used for filtration. |
4th parameter | Inverse relation. If true a list of executors from filtered right side of relation (if false then from filtered left side) is displayed. |
# ChooseActorByRelation
The component is similar to previous, but only actors are displayed (groups are filtered out).
# GroupMembers
An actor from a group is selected.
1st parameter | Variable of Actor type where the selected value is saved | |
2nd parameter | Variable of Group type where selector elements are got from | Indicated variable must be initialized and its name is indicated without quotation marks. It's also possible to indicate group name in quotation marks. |
3rd parameter | Display mode | Mode "all" means "select" output. Mode "as object list" is used for further usage by freemarker code. |
# AjaxGroupMembers
A group is selected with the help of linked lists.
1st parameter | Variable of Actor type where the selected value is saved |
2nd parameter | Variable of Group type where the selected value is saved |
# SelectFromList
The component is displayed as select element on the form.
1st parameter | A variable in which the selected value is saved |
2nd parameter | A list variable from which a value is selected |
Note. In some cases its unwise to use this component. For example if list values don't depend from the process (but is taken from the DB for example). It's better to implement a custom select component from the list with data from the given data source.
# MultipleSelectFromList
The component is displayed as several checkbox elements on the form.
1st parameter | A variable of list in which the selected values is saved |
2nd parameter | A list variable from which the values are selected
|
Note. In some cases its unwise to use this component. For example if list values don't depend from the process (but is taken from the DB for example). It's better to implement a custom select component from the list with data from the given data source.
# DisplayListElement
Each element from the list is displayed according to its type.
1st parameter | list variable |
2nd parameter | index starting from 0 or variable name without quotation marks or value with value@ prefix |
# DisplayMapElement
Each element from the map is displayed according to its type.
1st parameter | map variable |
2nd parameter | key, value or variable name without quotation marks or value with value@ prefix |
# ViewFile
1st parameter | File variable name from which the value is taken |
2nd parameter | Display mode |
Display modes
File contents (content) is displayed on the form. It is used for text files
File contents lenght (contentlength) in bytes is displayed on form
MIME type (contenttype) is displayed on form.
Image file contents (drawimage) is displayed as image on form.
As object (raw) for further usage in freemarker.
# Treeview support 4.1.2+
This component allows to use selection from tree which is build using server command. You should bind variables with tree and server command in form script. No parameters defined for this component.
After placing the tree component in form jquery.treeview plugin is activated. It can be used in form script as following:
$.fn.treeview.defaults.command = "serverCommandId"; $("#tree").treeview();
Optional capabilities are:
- selection change event handling
$("#tree").treeview({ onSelectionChanged: function(event) { alert("Selected value: " + $(this).text() + " (" + $(this).parent().attr("id") + ")"); } });
- full tree reloading
$("#tree").treeview("refresh");
# Treeview using database
For demo next table is used:
CREATE TABLE items (parent_id bigint, id bigint NOT NULL, label character varying(255), selectable boolean) INSERT INTO items(parent_id, id, label, selectable) VALUES (null, 1, 'Vegetables', false); INSERT INTO items(parent_id, id, label, selectable) VALUES (null, 2, 'Plants', false); INSERT INTO items(parent_id, id, label, selectable) VALUES (null, 3, 'Fruits', false); INSERT INTO items(parent_id, id, label, selectable) VALUES (3, 30, 'Apple', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (3, 31, 'Banana', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (3, 32, 'Cherry', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (30, 301, 'Gala', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (30, 302, 'Red Delicious', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (30, 303, 'Granny Smith', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (30, 304, 'Fuji', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (30, 305, 'Golden delicious', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (31, 310, 'Peeled', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (31, 311, 'Nicaraguan Nacatamales', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (31, 312, 'Kaeng yuak', true); INSERT INTO items(parent_id, id, label, selectable) VALUES (31, 313, 'Pisang goreng', true);
WFE Server has a configured datasource with JNDI name postgres-ds and extended application context (wfe.custom.system.context.xml), in which new command demoTreeView is registered:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <bean id="demoDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:postgres-ds"/> </bean> <bean id="demoTreeView" class="ru.runa.wfe.commons.web.SQLTreeViewCommand"> <property name="dataSource" ref="demoDataSource" /> <property name="tableName" value="items" /> <property name="parentIdColumnName" value="parent_id" /> <property name="idColumnName" value="id" /> <property name="labelColumnName" value="label" /> <property name="selectableColumnName" value="selectable" /> </bean> </beans>
In form we input variables selectedValue, selectedText.
In form script we bind tree with our command and tree selection with our variables:
$(document).ready(function() { $("input[name='selectedText']").attr("readonly", "true"); $("input[name='selectedText']").css("cursor", "pointer"); $("input[name='selectedText']").click(function() { $.treeDialog.dialog("open"); });
$.treeDialog = $("<div id='demotree' class='root'>").dialog({
modal: true, autoOpen: false, overlay: { backgroundColor: "#000", opacity: 0.5 }, create: function(event, ui) { $.fn.treeview.defaults.command = "demoTreeView"; $("#demotree").treeview(); }, open: function(event, ui) { $("#demotree").treeview("refresh"); }, height: 370, width: 570, title: "Choose from tree", buttons: {"Save": function() { var selected = $("#demotree .selected"); $("input[name='selectedValue']").val(selected.parent().attr("id")); $("input[name='selectedText']").val(selected.text()); $.treeDialog.dialog("close"); }} }); });
Process File:TreeDemo.par
# Other processes variables display 4.1.0+
If component is named TargetProcessCOMPONENTNAME, where COMPONENTNAME is any component name and indicate process id as first additional parameter then the component can work with the variables from the given process. All the rest of the parameters are the same as described above.