FreemarkerForms

Материал из RunaWFE
Перейти к навигации Перейти к поиску

Руководство по работе с формами

Версия 4.4.1

© 2015-2021, ООО "Процессные технологии", материалы этого документа распространяются свободно на условиях лицензии GNU FDL. RunaWFE Free является системой с открытым кодом и распространяется в соответствии с LGPL лицензией (http://www.gnu.org/licenses/lgpl.html).

# Обзор FTL форм

Форма представляет собой смесь языка freemarker и разметки HTML. Разрешается использовать любые инструкции freemarker, но графический редактор форм (на основе встроенного браузера) может их удалить. Расширением системы к freemarker являются компоненты, представляющие собой freemarker methods.

Подробнее (для добавления нового компонента) описано в руководствах разработчика редактора и сервера.

Замечание. Если название переменной содержит символы, работать с которыми в JavaScript или CSS не удаётся - можно использовать скриптовое название переменной, не содержащее таких символов.

# JavaScript

С каждой формой в среде разработки можно связать скрипт, который будет выполняться при её отображении.

# Использование внешних библиотек JS

Способ 1. Используя компонент формы

В качестве примера можно посмотреть ru.runa.wf.web.ftl.method.TreeviewSupportTag, который при генерации html содержит

<link rel='stylesheet' href='" + webHelper.getUrl("/css/jquery.treeview.css") + "'>
<script type='text/javascript' src='" + webHelper.getUrl("/js/jquery.treeview.js") + "'>c=0;</script>

Способ 2. Используя настройки 4.2.0+

В web.properties можно добавить ServerConfigurationGuide#task.form.external.js.libs.

Пример:

Конфигурация wfe.custom.web.properties:

task.form.external.js.libs = http://yastatic.net/raphael/2.1.0/raphael.min.js

Процесс Файл:Raphael form test.par

# Сокрытие или модификация кнопки выполнения задания

В некоторых случаях асинхронные задачи (например задача по отслеживанию статуса выполнения процесса) не должны выполняться пользователями вручную. Для них её можно скрыть с помощью скрипта формы:

$(document).ready(function() {
 $("input[name='submitButton']").hide();
});

Нижеприведённый скрипт формы делает кнопку неактивной и меняет её название:

$(document).ready(function() {
 $("input[name='submitButton']").val("This task should not be completed manually");
 $("input[name='submitButton']").attr("disabled", "disabled");
});

Пример процесса: Файл:HideSubmitButtonSample.par

# Настройка отображения текста

Компонент отображения переменной типа Текст генерирует textarea с атрибутом disabled. В IE он не поддаётся стилизации (нельзя ни добавить прокрутку, ни изменить цвет текста).

Остаётся альтернативный вариант генерации textarea с атрибутом readonly, но данный подход не реализован в проекте из-за: а) внешний вид не будет отличаться от элемента ввода, чем будет вводить пользователя в заблуждение б) внешне отличаться от других элементов отображения

Для того чтобы в форме использовать альтернативный вариант, можно

  • обеспечить замену атрибута disabled на readonly в элементе отображения текста, скрипт формы:
$(document).ready(function() {
 $("textarea[name='VARIABLE_NAME']").removeAttr("disabled");
 $("textarea[name='VARIABLE_NAME']").attr("readonly", "true");
});
  • обеспечить добавление атрибута readonly в элемент ввода текста, скрипт формы:
$(document).ready(function() {
 $("textarea[name='VARIABLE_NAME']").attr("readonly", "true");
});

# Доступ к переменным БП

Способ 1. Используя HTML формы

При отображении формы нужно использовать переменную (чтобы её значение попало на форму), после чего значение можно получить с помощью DOM-селектора.

Пример:

Допустим есть переменная "Цвет авто". Используем её на форме в режиме ввода

${InputVariable("Цвет авто")}

Тогда в скрипте формы можно получить её текущее значение так:

var variableValue = $("[name='Цвет авто']").val();

Способ 2. Используя Ajax-запрос 4.2.2+

По запросу на адрес "/wfe/variableJson.do" можно получить значение переменной в формате JSON. Аргументы вызова:

  • processId - ID процесса
  • taskId - ID задания (на странице задания доступна глобальная переменная id)
  • variableName - название переменной

Достаточно задать любой из ID.

Пример:

$.getJSON("/wfe/variableJson.do", { taskId : id, variableName : "Исполнитель[1]"}, function(data) {
 alert(data);
});

# Стилизация с помощью CSS

С каждым процессом можно связать каскадную таблицу стилей. В среде разработки это можно сделать в контекстном меню процесса (щёлкнув на полотне без узлов).


Хотя правила CSS и действуют в рамках всего процесса, но можно задать селекторы так, что они будут действовать по разному вплоть до 1-го элемента, например:

  • правило для всех элементов ввода типа Текст
.inputText {
 background-color: gray;
}
  • правило для всех элементов выбора на определённой форме с ID = ID23
.ID23 select {
 padding: 10px;
}
  • правило для всех элементов ввода типа Текст на определённой форме с ID = ID23
.ID23 .inputText {
 background-color: gray;
}
  • правило для отображения переменной Переменная 2 в виде текста на определённой форме с ID = ID23
.ID23 .Переменная_2 {
 border: 1px solid brown;
 display: block;
 padding: 10px;
}
  • правило для ввода переменной Переменная 2 в виде строки
input[name='Переменная 2'] {
 border: 1px solid brown;
}

Для идентификации переменных в CSS (класс и ID) требуется использовать скриптовое название.

# Работа со списками

Ввод и вывод переменных типа Список в HTML осуществляется с помощью указания индекса. Т.е. если переменная называется переменная2, её элементы будут отображены в виде элементов HTML с названиями переменная2[0], переменная2[1], переменная2[2], ...

# Вывод полей переменных типа (Пользователь, Исполнитель, Группа)

Вывод полей осуществляется стандартным способом с учётом синтаксиса freemarker. Например, для вывода Ф.И.О. из переменной запустившийПроцесс типа Пользователь

${запустившийПроцесс.fullName}
${запустившийПроцесс.active?string("да", "нет")}

Переменные с пробелами и иными спец. символами можно использовать со скриптовым названием

${запустивший_процесс.fullName} (для переменной запустивший процесс)

# Контекстные переменные 4.2.0+

Доступны объекты

Интерактивная форма (${context.interaction}, ru.runa.wfe.form.Interaction)

Использовать проверку в браузере	${context.interaction.useJSValidation?string("да", "нет")}
Список обязательных переменных		${context.interaction.requiredVariableNames}

Задание не для стартовой формы (${context.task}, ru.runa.wfe.task.dto.WfTask)

ID					${context.task.id}
Название 				${context.task.name}
Описание 				${context.task.description}
Название роли    			${context.task.swimlaneName}
Исполнитель				${context.task.owner.label}
Дата создания		    		${context.task.creationDate?datetime}
Получена по механизму эскалации?    	${context.task.escalated?string("да", "нет")}
Получена по механизму замещения?    	${context.task.acquiredBySubstitution?string("да", "нет")}

Определение процесса (${context.definition}, ru.runa.wfe.definition.dto.WfDefinition)

ID					${context.definition.id}
Название				${context.definition.name}
Описание				${context.definition.description}
Версия					${context.definition.version}
Загружено в систему			${context.definition.deployedDate?datetime}

Экземпляр процесса не для стартовой формы (${context.process}, ru.runa.wfe.execution.dto.WfProcess)

ID					${context.process.id}
Дата запуска				${context.process.startDate?datetime}
Дата завершения 			${context.process.endDate?datetime} (только для асинхронных заданий)
Иерархия подпроцессов			${context.process.hierarchyIds}

# Компоненты форм

Связь между Средой разработки и сервером осуществляется по названию компонента, регистрация требуется на стороне сервера в файле ftl.form.components.xml, а в Среде разработки - в plugin.xml. По согласшению названием компонента является простое название класса (без указания пакета).

Классы, расширяющие абстрактный класс ru.runa.wfe.commons.ftl.FormComponent, применяются при разработке форм для бизнес-процессов.

Нужно реализовать метод:

protected abstract Object renderRequest() throws Exception;

Обычно возвращается HTML, но с этим объектом далее можно проделывать любые операции с помощью freemarker expression language.

# Компоненты форм c поддержкой Ajax

Классы, расширяющие абстрактный класс AjaxFormComponent либо AjaxJsonFormComponent, применяются при разработке взаимодействия пользователя с сервером на форме с использованием технологии Ajax.

Методы:

protected abstract String renderRequest() throws Exception;
public void processAjaxRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

renderRequest вызывается при начальном отображении тега (показе формы), а processAjaxRequest вызывается с помощью JavaScript формы (либо периодически, либо при каких-либо действиях пользователя).

В качестве примера можно посмотреть класс AjaxGroupMembers, который в методе renderRequest отображает 2 комбо-бокса (список групп и список пользователей группы), а processAjaxRequest вызывается когда пользователь изменяет выбор группы в первом комбо-боксе.

В версии 4.3.0 в связи с изменением jsonUrl -> JSON_URL параметры tag, component и qualifier более не нужны.

Вызов метода processAjaxRequest можно сделать так из javascript:

$.getJSON(
 "JSON_URL", 
 { param1: value1, param2: value2 },
 function(data) {
   // handle json data returned from processAjaxRequest invocation
 }
);

В методе processAjaxRequest параметры доступны в виде строк с помощью request.getParameter("param1")

# Перечень существующих компонентов, не помеченных как устаревшие (deprecated)

# Ввод переменной (InputVariable)

Обеспечивает ввод переменной на форме. Генерируемый элемент ввода различается в зависимости от типа переменной.

${InputVariable("переменная")}
1-й параметр Название переменной любого типа, куда будет записано выбранное значение

Простые типы

тип переменной пример ввода
BooleanFormat (флаг) <input type="checkbox" name="переменная" class="inputBoolean" checked="checked" />
LongFormat (целое число), DoubleFormat (дробное число), BigDecimalFormat (число повышенной точности) <input type="text" name="переменная" class="inputNumber" value="33" />
DateFormat (дата) <input type="text" name="переменная" class="inputDateTime" style="width: 150px;" value="01.01.2013 17:57" />
DateTimeFormat (дата - время) <input type="text" name="переменная" class="inputDate" style="width: 100px;" value="17.03.2003" />
TimeFormat (время) <input type="text" name="переменная" class="inputTime" style="width: 50px;" value="15:35" />
StringFormat (строка) <input type="text" name="переменная" class="inputString" value="значение" />
TextFormat (текст) <textarea name="переменная" class="inputText">значение</textarea>
FileFormat (файл) <input type="file" name="переменная" class="inputFile" />
ExecutorFormat (исполнитель), GroupFormat (группа), ActorFormat (пользователь) <select name="переменная"><option value=""> ------------------------- </option><option value="ID303">Ф.И.О 1</option></select>

Список

Ввод списка осуществляется с помощью компонента EditList, который генерирует JavaScript для добавления и удаления элементов. Используется информация о типе компонентов списка для генерации соответствующего (см. таблицу выше) элемента ввода.

Ввод переменной типа Карта сейчас не поддерживается.

# Пример Autocomplete

На форме осуществляем ввод переменной месяц с помощью

${InputVariable("месяц")}

В скрипте формы подключаем выбор из предопределённых значений

var monthNames = ["январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"];
   
$(document).ready(function() {
 $("input[name='месяц']").autocomplete( {
  delay: 300,
  minLength: 0,
  source: monthNames
 });
 $("input[name='месяц']").focus(function() {
  $(this).autocomplete("search", $(this).val());
 });
});

# Пример Autocomplete с использованием БД 4.1.2+

Для демонстрации используется таблица:

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');

На сервере настроен источник данных с JNDI-именем postgres-ds и расширен контекст приложения с помощью конфигурационного файла wfe.custom.system.context.xml, в котором регистрируем команду demoListView:

<?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>

На форме осуществляем ввод переменных selectedValue, selectedText стандартным способом.

В скрипте формы подключаем выбор из предопределённых значений с использованием команды на сервере:

$(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());
	});
});

Процесс Файл:ListDemo.par

# Пример доступа к БД из формы 4.3.0+

Для демонстрации используется структура таблиц (postgresql syntax):

create table contracts(id integer, name text);
create table contract_orders(id integer, contract_id integer, name text, balance real);
create table order_requests(id integer, order_id integer, name text, price real);
insert into contracts(id, name) values (1, 'contract 1'), (2, 'contract 2'), (3, 'contract 3');
insert into contract_orders(id, contract_id, name, balance) values (1, 1, 'order 1 1', 2000), (2, 1, 'order 1 2', 3000), (3, 2, 'order 2 1', 4000), (4, 2, 'order 2 2', 1000) ;
insert into order_requests(id, order_id, name, price) values (1, 1, 'request 1 1 1', 500), (2, 1, 'request 1 1 2', 15000), (3, 2, 'request 1 2 1', 4000);

На сервере настроен источник данных с JNDI-именем jboss/datasources/TestDS и расширен контекст приложения с помощью конфигурационного файла wfe.custom.system.context.xml, в котором регистрируем команды:

<?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="testDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:jboss/datasources/TestDS"/>
    </bean>
    <bean id="sqlGetContracts" class="ru.runa.wfe.commons.web.SqlAjaxCommand">
        <property name="dataSource" ref="testDataSource" />
        <property name="sql">
           <value><![CDATA[ select * from contracts where name like ? ]]></value>
       </property>
        <property name="parameters">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Parameter">
                   <property name="name" value="filter" />
                   <property name="javaType" value="java.lang.String" />
               </bean>
           </list>
       </property>
        <property name="results">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Result">
                   <property name="propertyName" value="value" />
                   <property name="columnName" value="id" />
               </bean>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Result">
                   <property name="propertyName" value="label" />
                   <property name="columnName" value="name" />
               </bean>
           </list>
       </property>
    </bean>
    <bean id="sqlGetOrders" class="ru.runa.wfe.commons.web.SqlAjaxCommand">
        <property name="dataSource" ref="testDataSource" />
        <property name="sql">
           <value><![CDATA[ select * from contract_orders where contract_id = ? and name like ? ]]></value>
       </property>
        <property name="parameters">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Parameter">
                   <property name="name" value="contractId" />
                   <property name="javaType" value="java.lang.Long" />
               </bean>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Parameter">
                   <property name="name" value="filter" />
                   <property name="javaType" value="java.lang.String" />
               </bean>
           </list>
       </property>
        <property name="results">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Result">
                   <property name="propertyName" value="value" />
                   <property name="columnName" value="id" />
               </bean>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Result">
                   <property name="propertyName" value="label" />
                   <property name="columnName" value="name" />
               </bean>
           </list>
       </property>
   </bean>
   <bean id="sqlGetBalances" class="ru.runa.wfe.commons.web.SqlAjaxCommand">
        <property name="dataSource" ref="testDataSource" />
        <property name="sql">
           <value>
           <![CDATA[
               select (balance - (select sum(price) from order_requests where order_id = contract_orders.id)) as remainingBalance from contract_orders where contract_id = ? and id = ? 
           ]]>
           </value>
       </property>
        <property name="parameters">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Parameter">
                   <property name="name" value="contractId" />
                   <property name="javaType" value="java.lang.Long" />
               </bean>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Parameter">
                   <property name="name" value="orderId" />
                   <property name="javaType" value="java.lang.Long" />
               </bean>
           </list>
       </property>
        <property name="results">
           <list>
               <bean class="ru.runa.wfe.commons.web.SqlAjaxCommand.Result">
                   <property name="propertyName" value="remainingSum" />
                   <property name="columnName" value="remainingBalance" />
               </bean>
           </list>
       </property>
    </bean>
</beans>

На форме осуществляем ввод переменных стандартным способом.

В скрипте формы подключаем выбор из предопределённых значений с использованием команды на сервере:

$(document).ready(function() {
   $("input[name='selectedContractName']").css("cursor", "pointer");
   $("input[name='selectedContractName']").autocomplete({
       delay: 300,
       minLength: 0,
       source: function(request, response) {
           $.getJSON("/wfe/ajaxcmd?command=sqlGetContracts", {
               filter: request.term + "%"
           }, response);
       },
       select: function(event, ui) {
           if (ui.item.value != $("input[name='selectedContractId']").val()) {
               $("input[name='selectedContractId']").val(ui.item.value);
               $("input[name='selectedContractName']").val(ui.item.label);
               $("input[name='selectedOrderId']").val("");
               $("input[name='selectedOrderName']").val("");
               $("input[name='remainingSum']").val("");
           }
           return false;
       }
   });
   $("input[name='selectedContractName']").focus(function() {
       $(this).autocomplete("search", $(this).val());
   });
   $("input[name='selectedOrderName']").css("cursor", "pointer");
   $("input[name='selectedOrderName']").autocomplete({
       delay: 300,
       minLength: 0,
       source: function(request, response) {
           var selectedContractId = $("input[name='selectedContractId']").val();
           if (selectedContractId) {
               $.getJSON("/wfe/ajaxcmd?command=sqlGetOrders", {
                   contractId: selectedContractId,
                   filter: request.term + "%"
               }, response);
           } else {
               response([{"value": "", "label": "Сначала выберите договор"}]);
           }
       },
       select: function(event, ui) {
           if (!ui.item.value) {
               return false;
           }
           if (ui.item.value != $("input[name='selectedOrderId']").val()) {
               $("input[name='selectedOrderId']").val(ui.item.value);
               $("input[name='selectedOrderName']").val(ui.item.label);
               $("input[name='remainingSum']").val("");
               $.getJSON("/wfe/ajaxcmd?command=sqlGetBalances", {
                       contractId: $("input[name='selectedContractId']").val(),
                       orderId: $("input[name='selectedOrderId']").val()
                   }, function(data) {
                       $("input[name='remainingSum']").val(data[0].remainingSum ? data[0].remainingSum : "null");
                   }
               );
           }
           return false;
       }
   });
   $("input[name='selectedOrderName']").focus(function() {
       $(this).autocomplete("search", $(this).val());
   });
   $("input[name='selectedContractId'], input[name='selectedOrderId'], input[name='remainingSum']").attr("readonly", "true").attr("placeholder", "read-only");
});

Процесс Файл:Issue377.par

# Отображение переменной (DisplayVariable)

Обеспечивает отображение переменной в зависимости от типа.

${DisplayVariable("переменная", "false")}
1-й параметр Название переменной любого типа, откуда будет взято отображаемое значение
2-й параметр Режим отображения: false = в виде строки, true = в виде неактивного элемента ввода

Режим отображения: в виде строки

Вывод в этом режиме зависит от типа переменной.

Простые типы

тип переменной вывод
BooleanFormat (флаг) true или false
LongFormat (целое число), DoubleFormat (дробное число), BigDecimalFormat (число повышенной точности) Число в виде строки
DateFormat (дата) Дата в виде строки
DateTimeFormat (дата - время) Дата со временем в виде строки
TimeFormat (время) Время в виде строки
StringFormat (строка) Строка
TextFormat (текст) Текст в виде строки
FileFormat (файл) Ссылка для скачивания файла
ExecutorFormat (исполнитель), GroupFormat (группа), ActorFormat (пользователь) Ссылка на исполнителя в системе при наличии прав на чтение, иначе Ф.И.О.

Список

Вывод списка форматирован в соответствии с шаблоном [элемент1, элемент2], в котором элементы форматируются в зависимости от типа элемента.

Вывод переменной типа Карта сейчас не поддерживается.

# Подсказки при отображении переменных типа (Пользователь, Исполнитель, Группа) 4.1.2+

Для данных типов возможно генерировать подсказку к ссылкам на формах. Для этого нужно создать файл с название ${type}.tooltip.template и расположить его в classpath приложения.

тип переменной название файла с шаблоном подсказки
исполнитель ru.runa.wfe.user.Executor.tooltip.template
пользователь ru.runa.wfe.user.Actor.tooltip.template
группа ru.runa.wfe.user.Group.tooltip.template

В этих файлах нужно написать шаблон подсказки с синтаксисом freemarker и разметкой HTML, в котором можно использовать определённые поля.

Пример шаблона для пользователя:

<#if object.description?? && object.description?length != 0>
 Описание: <b>${object.description}<br>
</#if>
<#if object.phone?? && object.phone?length != 0>
 Телефон: <b>${object.phone}<br>
</#if>
<#if object.email?? && object.email?length != 0>
 Email: <b>${object.email}
</#if>

Режим отображения: в виде неактивного элемента ввода

Вывод в этом режиме аналогичен выводу компонента ввода переменной, за исключением того, что элемент вывода не активен.

# Редактирование списка переменных пользовательского типа (EditUserTypeList) 4.3.0+

Редактирование переменной типа Список(Пользовательский тип) в виде таблицы.

${EditUserTypeList("Список", "true", "true", "true", "ID", "Название")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр добавление элементов в список разрешено?
3-й параметр удаление элементов из списка разрешено?
4-й параметр редактирование существующих элементов списка разрешено?
5-й параметр (множественный) список атрибутов пользовательского типа для отображения в таблице (если не задан - то выводятся все)

# Отображение списка переменных пользовательского типа (DisplayUserTypeList) 4.3.0+

Отображение переменной типа Список(Пользовательский тип) в виде таблицы.

${DisplayUserTypeList("Список", "true", "ID", "Название")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр режим отображения: false = в виде строки, true = в виде неактивного элемента ввода
3-й параметр (множественный) список атрибутов пользовательского типа для отображения в таблице (если не задан - то выводятся все)

# Множественный выбор из списка переменных пользовательского типа (MultipleSelectFromUserTypeList) 4.3.0+

${MultipleSelectFromUserTypeList("Список", "Весь список", "ID", "Название")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр переменная, из которой формируется таблица.
3-й параметр (множественный) список атрибутов пользовательского типа для отображения в таблице (если не задан - то выводятся все)

# Выбрать исполнителя по отношению (ChooseExecutorFromRelation)

Отображает список выбора исполнителя из одной части всего отношения.

${ChooseExecutorFromRelation("Переменная", "отношение", "false")}
1-й параметр Название переменной типа Исполнитель для сохранения результата.
2-й параметр Название отношения, может быть задано с помощью переменной или введено вручную (с префиксом value@).
3-й параметр Использовать ли обратное отношение. В случае true отображаются исполнители из правой части, иначе - из левой.

# Выбрать исполнителя по отношению с параметром (ChooseExecutorByRelation)

Отображает список выбора исполнителя из одной части отфильтрованного по параметру отношения.

${ChooseExecutorByRelation("Переменная", "отношение", "название группы", "false")}
1-й параметр Название переменной типа Исполнитель для сохранения результата.
2-й параметр Название отношения, может быть задано с помощью переменной или введено вручную (с префиксом value@).
3-й параметр Параметр отношения для фильтрации пар по правой части (в случае обратного отношения - по левой). Может быть задан переменной типа Исполнитель или иметь строковое значение, соответствующее названию группы или пользователя (с префиксом value@). Для фильтрации используется параметр вместе со всеми группами, в которые он входит.
4-й параметр Использовать ли обратное отношение. В случае true отображаются исполнители из правой части (параметр ), иначе - из левой.

# Выбрать пользователя по отношению с параметром (ChooseActorByRelation)

Компонент идентичен предыдущему, за исключением того что выбрать можно только из списка пользователей (группы не отображаются).

# Выбор из членов группы (GroupMembers)

Осуществляется ввод пользователя из предопределенной группы.

${GroupMembers("переменная1", переменная2, "all")}
1-й параметр Название переменной типа Пользователь, куда будет записано выбранное значение.
2-й параметр Название переменной типа Группа, откуда будут взяты сотрудники. При указании переменной она должна быть проинициализирована и ее название указывается без скобок. Также можно указать название группы в скобках.
3-й параметр Режим отображения Режим Полный список означает вывод select. Режим Как список объектов используется для дальнейшего использования инструкциями freemarker.

# Выбор из членов группы (ajax) (AjaxGroupMembers)

Осуществляется ввод группы и пользователя из связанных списков.

${AjaxGroupMembers("переменная1", "переменная2")}
1-й параметр Название переменной типа Пользователь, куда будет записано выбранное значение.
2-й параметр Название переменной типа Группа, куда будет записано выбранное значение.

# Выбор значения из списка (OptionSelectFromVariableList)

Компонент назывался SelectFromList до версии 4.3.0.

Представление на форме в виде селект-бокса.

${OptionSelectFromVariableList("Переменная1", "Переменная2")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр переменная типа Список, представляющая все возможные значения для выбора.

В некоторых ситуациях использование данного компонента будет нецелесообразным, например в случае когда выбор осуществляется из списка, который не зависит от процесса (например выборка из БД вне контекста процесса). Будет лучше реализовать собственный компонент выбора из списка, полученного из данного источника данных..

# Выбор значения из списка (CheckboxSelectFromVariableList) 4.3.0+

Представление на форме в виде списка чек-боксов.

${CheckboxSelectFromVariableList("Переменная1", "Переменная2", "horizontal")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр переменная типа Список, представляющая все возможные значения для выбора.
3-й параметр расположение списка опций: по-горизонтали либо по-вертикали (по умолчанию)

В некоторых ситуациях использование данного компонента будет нецелесообразным, например в случае когда выбор осуществляется из списка, который не зависит от процесса (например выборка из БД вне контекста процесса). Будет лучше реализовать собственный компонент выбора из списка, полученного из данного источника данных..

# Выбор значения из предопределённого списка (SelectFromValueList)

Представление на форме в виде списка радио-кнопок.

${SelectFromValueList("Переменная1", "Вариант1", "Вариант2")}
1-й параметр переменная, в которую будет записано выбранное значение.
2-й параметр (множественный) список вариантов

# Множественный выбор значений из списка (MultipleSelectFromList)

Представление на форме в виде нескольких чек-боксов.

${MultipleSelectFromList("Переменная1", "Переменная2", "horizontal")}
1-й параметр переменная типа Список, в которую будут записаны выбранные значения
2-й параметр переменная типа Список, представляющая все возможные значения для выбора
3-й параметр расположение списка опций: по-горизонтали либо по-вертикали (по умолчанию)

В некоторых ситуациях использование данного компонента будет нецелесообразным, например в случае когда выбор осуществляется из списка, который не зависит от процесса (например выборка из БД вне контекста процесса). Будет лучше реализовать собственный компонент выбора из списка, полученного из данного источника данных..

# Отобразить элемент списка (DisplayListElement)

Отображение элемента списка происходит на основании типа элемента, по аналогии с отображением переменной.

${DisplayListElement("Переменная4", "2")}
1-й параметр переменная списка.
2-й параметр индекс, начиная с 0, может быть задано с помощью переменной или введено вручную (с префиксом value@).

# Отобразить элемент карты (DisplayMapElement)

Отображение элемента карты происходит на основании типа элемента, по аналогии с отображением переменной.

${DisplayMapElement("Переменная3", Переменная1)}
1-й параметр переменная карты.
2-й параметр ключ, может быть задано с помощью переменной или введено вручную (с префиксом value@).

# Просмотр файла (ViewFile)

${ViewFile("Переменная2", "content")}
1-й параметр Название файловой переменной, из которой берется значение
2-й параметр Режим отображения

Режим отображения

Текст файла (content): отображает на форме содержимое файла. Используется для текстовых файлов.

Длина файла (contentlength): отображает на форме длину файла в байтах.

MIME тип файла (contenttype): отображает на форме тип содержимого файла.

Отобразить изображение (drawimage): отображает на форме содержимое графического файла в виде картинки.

Как объект (raw): для дальнейшего использования в freemarker.

# Выбор из иерархического справочника (TreeviewSupport) 4.1.2+

Параметров у данного компонента нет, он предоставляет лишь возможность использовать дерево с выбором. Чтобы сделать рабочий пример нужно в скрипте формы связать дерево с командой на сервере и передать данные выбранного элемента в нужные переменные.

После расположения данного компонента на форме активируется плагин jquery.treeview, который можно использовать:

$.fn.treeview.defaults.command = "serverCommandId";
$("#tree").treeview();

Опциональными возможностями являются:

  • подписка на событие изменения выделения при создании дерева
$("#tree").treeview({
 onSelectionChanged: function(event) {
  alert("Selected value: " + $(this).text() + " (" + $(this).parent().attr("id") + ")");
 }
});
  • полная перезагрузка дерева
$("#tree").treeview("refresh");

# Выбор из иерархического справочника в БД

Для демонстрации используется таблица:

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);

На сервере настроен источник данных с JNDI-именем postgres-ds и расширен контекст приложения с помощью конфигурационного файла wfe.custom.system.context.xml, в котором регистрируем команду demoTreeView:

<?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>

На форме осуществляем ввод переменных selectedValue, selectedText стандартным способом.

В скрипте формы делаем связку дерева с командой на сервере и передаём выбранное значение в переменные:

$(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");
		}}
	});
});

Процесс Файл:TreeDemo.par

# Редактировать связанные списки (EditLinkedLists)

${EditLinkedLists("true", "true", "true", "Переменная1", "Переменная2")}

Первые 3 параметра фиксированные:

1-й параметр Разрешать добавление объектов в списки
2-й параметр Разрешать редактирование существующих объектов в списки
3-й параметр Разрешать удаление существующих объектов из списков

Далее идут параметры для указания какие списки нужно редактировать.

Отображается в виде таблицы, где указанные списки отображаются в виде её столбцов (ввод элементов аналогичен вводу переменной в зависимости от типа элемента).

Для каждого столбца, заданного переменной название списка: в случае присутствия в процессе переменной с названием название списка_header, её значение используется для именования столбца.

Данный компонент осуществляет рассылку событий onRowAdded, onRowRemoved на корневом элементе <TABLE id="editLinkedLists"> при добавлении и удалении строк на форме соответственно.

Пример скрипта, запрещающего редактирование определённого столбца

$(document).ready(function() {
 $("textarea[name='тексты_авторов']").attr("readonly", "true");
 $("input[name='авторы']").attr("readonly", "true");
});

# Отобразить связанные списки (DisplayLinkedLists)

${DisplayLinkedLists("Переменная1", "Переменная2", "Переменная3", "Переменная4")}

Компонент имеет динамическое количество параметров, в зависимости от того - сколько списков нужно отобразить.

Отображается в виде таблицы, где указанные списки отображаются в виде её столбцов (вывод элементов аналогичен отображению переменной в зависимости от типа компонента). Размер списков может быть разным.

Для каждого столбца, заданного переменной название списка: в случае присутствия в процессе переменной с названием название списка_header, её значение используется для именования столбца.

Пример отображения:

<TABLE class="displayLinkedLists" rowscount="1"> <TR class="header"> <TD><TD><B>список даты и времени выхода</B></TD><TD><B>контактные телефоны</B></TD><TD><B>время входа</B></TD><TD><B>время выхода</B></TD> </TR> <TR row="0"> <TD column="0"> <INPUT class="inputDateTime" disabled="true" name="список даты и времени выхода[0]" style="width: 150px;" type="text" value="11.08.2013 10:00"> </TD> <TD column="1"> <INPUT class="inputString" disabled="true" name="контактные телефоны[0]" type="text" value="139129431"> </TD> <TD column="2"> <INPUT class="inputTime" disabled="true" name="время реального входа[0]" style="width: 50px;" type="text"> </TD> <TD column="3"> <INPUT class="inputTime" disabled="true" name="время реального выхода[0]" style="width: 50px;" type="text"> </TD> </TR> </TABLE>

Пример скрипта, подкрашивающего вышеприведенную таблицу

$(document).ready(function() {
 var rows = $(".displayLinkedLists").attr("rowsCount");
 for (var row=0; row<rows; row++) {
  var inTime = $("input[name='время реального входа["+row+"]']").val();
  var outTime = $("input[name='время реального выхода["+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("Переменная1", "Переменная2")}

Компонент имеет динамическое количество параметров, в зависимости от того - сколько карт нужно отобразить.

Отображается в виде таблицы, где указанные карты отображаются в виде её столбцов (вывод элементов аналогичен отображению переменной в зависимости от типа значения). Размер карт может быть разным. Осуществляется вывод только значений, связь между ними осуществляется по ключам.

Для каждого столбца, заданного переменной название карты: в случае присутствия в процессе переменной с названием название карты_header, её значение используется для именования столбца.

# Отображение переменных из другого процесса 4.1.0+

Если использовать в качестве названия компонента TargetProcessCOMPONENTNAME, где COMPONENTNAME - название любого компонента, а также использовать первый дополнительный параметр ID процесса, то компонент будет работать с отображением переменных из указанного процесса. Все остальные параметры компонента остаются без изменений.