aconfgen: поддержка Alfresco 4.0

Немного обновил aconfgen: теперь он должен корректно работать с  Alfresco 4.0.

В новой версии изменения следующие:

  • добавлена возможность работы с процессами, написанными для Activiti
  • добавлена возможность генерировать локализацию модели

Итак, по порядку о нововведениях. Пусть у нас есть вот такое описание процесса: 

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://activiti.org/bpmn20">
  <process id="example" name="Parallel Review And Approve Activiti Process">
    <extensionElements>
      <activiti:executionListener event="start" class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener">
        <activiti:field name="script">
          <activiti:string>execution.setVariable('wf_approveCount', 0);
execution.setVariable('wf_actualPercent', 0);
execution.setVariable('wf_reviewerCount', bpm_assignees.size());
execution.setVariable('wf_requiredPercent', wf_requiredApprovePercent);</activiti:string>
        </activiti:field>
      </activiti:executionListener>
    </extensionElements>
    <startEvent id="start" name="Start" activiti:formKey="wf:submitParallelReviewTask"></startEvent>
    <userTask id="reviewTask" name="Review Task" activiti:assignee="${reviewAssignee.properties.userName}" activiti:formKey="wf:activitiReviewTask">
      <extensionElements>
        <activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
          <activiti:field name="script">
            <activiti:string>if (typeof bpm_workflowDueDate != 'undefined') task.setVariableLocal('bpm_dueDate', bpm_workflowDueDate);
if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority;</activiti:string>
          </activiti:field>
        </activiti:taskListener>
        <activiti:taskListener event="complete" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
          <activiti:field name="script">
            <activiti:string>if(task.getVariableLocal('wf_reviewOutcome') == 'Approve') {
var newApprovedCount = wf_approveCount + 1;
var newApprovedPercentage = (newApprovedCount / wf_reviewerCount) * 100;
 
execution.setVariable('wf_approveCount', newApprovedCount);
execution.setVariable('wf_actualPercent', newApprovedPercentage);
}</activiti:string>
          </activiti:field>
        </activiti:taskListener>
      </extensionElements>
      <multiInstanceLoopCharacteristics isSequential="false">
        <loopDataInputRef>bpm_assignees</loopDataInputRef>
        <inputDataItem name="reviewAssignee"></inputDataItem>
        <completionCondition>${wf_actualPercent >= wf_requiredApprovePercent}</completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <exclusiveGateway id="reviewDecision" name="Review Decision"></exclusiveGateway>
    <userTask id="approved" name="Document Approved" activiti:assignee="${initiator.properties.userName}" activiti:formKey="wf:approvedParallelTask">
      <documentation>
                The document was reviewed and approved.
            </documentation>
      <extensionElements>
        <activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
          <activiti:field name="script">
            <activiti:string>if (typeof bpm_workflowDueDate != 'undefined') task.setVariableLocal('bpm_dueDate', bpm_workflowDueDate);
if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority;
 
// Set parallel review params on task, to be kept in history
task.setVariableLocal('wf_reviewerCount', wf_reviewerCount);
task.setVariableLocal('wf_requiredPercent', wf_requiredPercent);
task.setVariableLocal('wf_actualPercent', wf_actualPercent);
task.setVariableLocal('wf_approveCount', wf_approveCount);</activiti:string>
          </activiti:field>
        </activiti:taskListener>
      </extensionElements>
    </userTask>
    <userTask id="rejected" name="Document Rejected" activiti:assignee="${initiator.properties.userName}" activiti:formKey="wf:rejectedParallelTask">
      <documentation>
                The document was reviewed and rejected.
            </documentation>
      <extensionElements>
        <activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
          <activiti:field name="script">
            <activiti:string>if (typeof bpm_workflowDueDate != 'undefined') task.setVariableLocal('bpm_dueDate', bpm_workflowDueDate);
if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority;
 
// Set parallel review params on task, to be kept in history
task.setVariableLocal('wf_reviewerCount', wf_reviewerCount);
task.setVariableLocal('wf_requiredPercent', wf_requiredPercent);
task.setVariableLocal('wf_actualPercent', wf_actualPercent);
task.setVariableLocal('wf_approveCount', wf_approveCount);</activiti:string>
          </activiti:field>
        </activiti:taskListener>
      </extensionElements>
    </userTask>
    <endEvent id="end" name="End"></endEvent>
    <sequenceFlow id="flow1" name="" sourceRef="start" targetRef="reviewTask"></sequenceFlow>
    <sequenceFlow id="flow2" name="" sourceRef="reviewTask" targetRef="reviewDecision"></sequenceFlow>
    <sequenceFlow id="flow3" name="" sourceRef="reviewDecision" targetRef="approved">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${wf_actualPercent >= wf_requiredApprovePercent}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" name="" sourceRef="reviewDecision" targetRef="rejected"></sequenceFlow>
    <sequenceFlow id="flow5" name="" sourceRef="approved" targetRef="end"></sequenceFlow>
    <sequenceFlow id="flow6" name="" sourceRef="rejected" targetRef="end"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_example"><!-- Диаграмма вырезана за ненадобностью --></bpmndi:BPMNDiagram>
</definitions>

По-моему, писать такие процессы «руками» теперь просто нереально. Ну да ладно. При помощи команды 

./aconfgen.py -mMdiafcr ~/example.bpmn20.xml

 получаем вот такую модель: 

<?xml version="1.0" encoding="utf-8"?>
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="wf:samplemodel">
  <!--Model metadata-->
  <description>Task model for example.bpmn20.xml</description>
  <author>lx</author>
  <version>1.0</version>
  <!--Import necessary namespaces-->
  <imports>
    <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
    <import uri="http://www.alfresco.org/model/bpm/1.0" prefix="bpm"/>
  </imports>
  <!--List of found namespaces in process definition-->
  <namespaces>
    <namespace prefix="wf" uri="https://github.com/fufler/aconfgen/prefix/wf"/>
  </namespaces>
  <!--List of types-->
  <types>
    <!--Type for wf:submitParallelReviewTask task-->
    <type name="wf:submitParallelReviewTask">
      <parent>bpm:startTask</parent>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>wf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for wf:rejectedParallelTask task-->
    <type name="wf:rejectedParallelTask">
      <parent>bpm:activitiOutcomeTask</parent>
      <!--Add outcome property for activiti tasks-->
      <properties>
        <property name="wf:rejectedParallelTaskOutcome">
          <type>d:text</type>
          <default>done</default>
          <constraints>
            <constraint type="LIST" name="wf:rejectedParallelTaskOutcomeConstraint">
              <parameter name="allowedValues">
                <list>
                  <value>done</value>
                </list>
              </parameter>
            </constraint>
          </constraints>
        </property>
      </properties>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
        <property name="bpm:outcomePropertyName">
          <default>{https://github.com/fufler/aconfgen/prefix/wf}rejectedParallelTask</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>wf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for wf:activitiReviewTask task-->
    <type name="wf:activitiReviewTask">
      <parent>bpm:activitiOutcomeTask</parent>
      <!--Add outcome property for activiti tasks-->
      <properties>
        <property name="wf:activitiReviewTaskOutcome">
          <type>d:text</type>
          <default>approved</default>
          <constraints>
            <constraint type="LIST" name="wf:activitiReviewTaskOutcomeConstraint">
              <parameter name="allowedValues">
                <list>
                  <value>approved</value>
                  <value>rejected</value>
                </list>
              </parameter>
            </constraint>
          </constraints>
        </property>
      </properties>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
        <property name="bpm:outcomePropertyName">
          <default>{https://github.com/fufler/aconfgen/prefix/wf}activitiReviewTask</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>wf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for wf:approvedParallelTask task-->
    <type name="wf:approvedParallelTask">
      <parent>bpm:activitiOutcomeTask</parent>
      <!--Add outcome property for activiti tasks-->
      <properties>
        <property name="wf:approvedParallelTaskOutcome">
          <type>d:text</type>
          <default>done</default>
          <constraints>
            <constraint type="LIST" name="wf:approvedParallelTaskOutcomeConstraint">
              <parameter name="allowedValues">
                <list>
                  <value>done</value>
                </list>
              </parameter>
            </constraint>
          </constraints>
        </property>
      </properties>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
        <property name="bpm:outcomePropertyName">
          <default>{https://github.com/fufler/aconfgen/prefix/wf}approvedParallelTask</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>wf:customAspect</aspect>
      </mandatory-aspects>
    </type>
  </types>
  <!--Custom aspect definition sample-->
  <aspects>
    <aspect name="wf:customAspect">
      <title>Custom aspect sample</title>
      <properties>
        <property name="wf:customProperty">
          <type>d:string</type>
          <mandatory>false</mandatory>
          <multiple>false</multiple>
        </property>
      </properties>
    </aspect>
  </aspects>
</model>

Как видно из примера, для Activiti-процессов в модели автоматические генерируются bpm:outcomePropertyName и соответствующий constraint, для JPDL-процессов всё осталось попрежнему.  Constraint генерируется следующим образом: если после таска в описании есть Exclusive Gateway, то значениями constraint становятся названия всех тасков, непосредственно следующих за ним; если следующий элемент не Exclusive Gateway, но в constraint попадает только одно значение — «done».

В версии 4.0 немного изменилась схема локализации: теперь большая часть локализации генерируется по модели, а не по описанию процесса.  По описанию Activiti-процесса генерируются всего две строки: 

./aconfgen.py -W ~/example.bpmn20.xml
example.workflow.title=
example.workflow.description=

 Зато достаточно много строк можно нагенерировать по модели:

./aconfgen.py -mMdia ~/example.bpmn20.xml | ./aconfgen.py -Z -
wf_samplemodel.title=
wf_samplemodel.description=
wf_samplemodel.type.wf_submitParallelReviewTask.title=
wf_samplemodel.type.wf_submitParallelReviewTask.description=
wf_samplemodel.type.wf_rejectedParallelTask.title=
wf_samplemodel.type.wf_rejectedParallelTask.description=
wf_samplemodel.type.wf_activitiReviewTask.title=
wf_samplemodel.type.wf_activitiReviewTask.description=
wf_samplemodel.type.wf_approvedParallelTask.title=
wf_samplemodel.type.wf_approvedParallelTask.description=
wf_samplemodel.aspect.wf_customAspect.title=
wf_samplemodel.aspect.wf_customAspect.description=
wf_samplemodel.property.wf_rejectedParallelTaskOutcome.title=
wf_samplemodel.property.wf_rejectedParallelTaskOutcome.description=
wf_samplemodel.property.wf_activitiReviewTaskOutcome.title=
wf_samplemodel.property.wf_activitiReviewTaskOutcome.description=
wf_samplemodel.property.wf_approvedParallelTaskOutcome.title=
wf_samplemodel.property.wf_approvedParallelTaskOutcome.description=
wf_samplemodel.property.wf_customProperty.title=
wf_samplemodel.property.wf_customProperty.description=
listconstraint.wf_rejectedParallelTaskOutcomeConstraint.done=
listconstraint.wf_activitiReviewTaskOutcomeConstraint.approved=
listconstraint.wf_activitiReviewTaskOutcomeConstraint.rejected=
listconstraint.wf_approvedParallelTaskOutcomeConstraint.done=

 Новая версия скрипта пока что находится в состоянии альфа-версии, так не было возможности потестировать в «боевых» условиях. Вроде вывод скрипта сходится с примерами от Jeff Potts и тем, что написано на вики.  Об ошибках пишите в комменты — исправлю.

Адрес проживания скрипта остался прежним. Новую версию смотрите в бранче 4.0.

 

2483

Комментарии

Не работает :(

  File "aconfgen.py", line 236
    for x in ctx.xpathEval('/defaultns:definitions/defaultns:process/*[@activiti:formKey!=""]')
      ^
SyntaxError: invalid syntax
 
fufler аватар
Как запускаете?
Какая версия python?
Какая версия aconfgen?