FreemarkerForms

From RunaWFE
Revision as of 11:06, 21 March 2015 by doc>Dofs (→‎Using setting {{Since|4.2.0}})
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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

${InputVariable("variable")}
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.

${DisplayVariable("variable", "false")}
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.

${ChooseExecutorFromRelation("Variable", "Relation", "false")}
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.

${ChooseExecutorByRelation("Variable", "Relation", "Group name", "false")}
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.

${GroupMembers("variable1", variable2, "all")}
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.

${AjaxGroupMembers("variable1", "variable2")}
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.

${SelectFromList("variable1", "varaible2")}
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.

${MultipleSelectFromList("variable1", "variable2")}
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.

${DisplayListElement("variable4", "2")}
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.

${DisplayMapElement("variable3", variable1)}
1st parameter map variable
2nd parameter key, value or variable name without quotation marks or value with value@ prefix

# ViewFile

${ViewFile("variable2", "content")}
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.