aconfgen: генератор конфигурационных файлов Alfresco

Недавно выложил на github питоновый скрипт для генерации конфигурационных файлов Alfresco. Подробности под катом.

В прошлых статьях я уже писал,какие конфигурационные файлы необходимы для создания нового бизнес-процесса в Alfresco. Постоянное написание однотипных конфигов несколько утомляет, поэтому в голове родилась идея написать скрипт, несколько упрощающий жизнь. Итак, скрипт умеет следующее

  • по описанию процесса генерировать список swimlane-ов
  • по описанию процесса генерировать модель  процесса (task model)
  • по описанию процесса генерировать скроки для локализации
  • по модели процесса генерировать конфиг для отрисовки форм в Share (share-config-custom.xml)
  • по конфигу для форм Share генерировать строки для локализации

Следует помнить, что сгенерированные конфиги всё ещё требуют ручной обработки, но это сделать гораздо проще, чем писать с нуля. Ниже приведен список параметров командной строки, поддерживаемых скриптом, и некоторые примеры использования:

Использование: aconfgen.py [-h] (-s | -m | -w | -W | -e) [-M] [-d] [-i] [-a]
                   [-n PROCESS_NAME] [-l] [-S] [-f] [-c] [-r]
                   XML
 
Генерирует заготовки конфигурационных файлов Alfresco, используя описание процесса/
модель процесса/конфиг Share
 
Обязательные аргументы:
  XML                   XML файл, содержащий описание процесса на языке jPDL/
                        модель процесса/конфиг Share (используйте '-' для чтения stdin)
 
Опции:
  -h, --help            показать эту справку и выйти
  -s, --swimlanes       сгенерировать список swimlane-ов по описанию процесса
  -m, --model           сгенерировать заготовки модели процесса
  -w, --workflow-ui     сгенерировать заготовку share-config-custom.xml для
                        отображения интерфейса процесса
  -W, --workflow-i18n   сгенерировать заготовку пакета интернационализации процесса
  -e, --share-i18n      сгенерировать заготовку пакета интернационализации Share
 
Опции генерации модели процесса:
  -M, --mandatory-aspects
                        добавить секцию mandatory-aspects внутрь каждой секции type
  -d, --metadata        добавить метаданные в модель
  -i, --item-actions    добавить секцию item-actions внутрь каждой секции type
  -a, --aspect          добавить заготовку описания аспекта
 
Опции генерации конфига Share для отображения форм процесса:
  -n PROCESS_NAME, --process-name PROCESS_NAME
                        имя процесса для использования в конфиге (префикс jbpm$ будет
                        добавлен автоматически)
  -l, --label-id        добавить атрибут label-id в каждый узел field
  -S, --sets            добавить описания контейнеров(set) пользовательских элементов управления
 
Параметры вывода:
  -f, --format          отформатировать XML пробелами (часто требуется указание -r)
  -c, --comments        добавлять комментарии
  -r, --remove-blanks   удалить все пустые узлы из итогового XML
 

Возьмём за основу XML из старой статьи (такой XML вполне можно получить при помощи eclipse-плагина):

<?xml version="1.0" encoding="UTF-8"?>
 
<!-- Начало описания процесса -->
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="tcwf:publishpaper">
 
  <!-- Начальный узел процесса -->
  <start-state name="Start">
    <!-- Задание для инициатора -->
    <task name="tcwf:submitTask" swimlane="initiator" />
    <!-- Подать статью на рассмотрение -->
    <transition name="" to="Submit" />
  </start-state>
 
  <!-- Подача статьи на рассмотрение -->
  <node name="Submit">
    <!-- Передать корректору -->
    <transition name="" to="CorrectTask"/>
  </node>
 
  <!-- Корректирование статьи -->
  <task-node name="CorrectTask">
    <!-- Задача для корректора -->
    <task name="tcwf:correctTask" swimlane="corrector" />
    <!-- Одобрить статью -->
    <transition name="approve" to="EditTask">
      <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
        <!-- Отправляем уведомление -->
        <script>
          <![CDATA[
            logger.log("PublishPaperProcess: Approved");
            if (tcwf_notifyMe)
            {
              var mail = actions.create("mail");
              mail.parameters.to = initiator.properties["cm:email"];
              mail.parameters.subject = "Публикация статьи";
              mail.parameters.text = "Добрый день.nnВаша статья прошла проверку редактора и отправлена техническому редактору на рассмотрение.";
              mail.execute(bpm_package);
              }
            ]]>
        </script>
      </action>
    </transition>
    <!-- Отклонить статью -->
    <transition name="reject" to="ReviseTask">
      <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
        <!-- Отправляем уведомление -->
        <script>
          <![CDATA[
            logger.log("PublishPaperProcess: Rejected");
            if (tcwf_notifyMe)
            {
              var mail = actions.create("mail");
              mail.parameters.to = initiator.properties["cm:email"];
              mail.parameters.subject = "Публикация статьи";
              mail.parameters.text = "Добрый день.nnВаша статья не прошла проверку редактора и нуждается в доработке. nnКомментарии:n"+ tcwf_comments;
              mail.execute(bpm_package);
            }
            ]]>
        </script>
      </action>
    </transition>
  </task-node>
 
  <!-- Техническое редактирование статьи -->
  <task-node name="EditTask">
    <!-- Задача для редактора -->
    <task name="tcwf:editTask" swimlane="editor" />
    <!-- Одобрить статью -->
    <transition name="approve" to="PublishTask">
      <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
        <!-- Отправляем уведомление -->
        <script>
          <![CDATA[
            logger.log("PublishPaperProcess: Approved2");
            if (tcwf_notifyMe)
            {
              var mail = actions.create("mail");
              mail.parameters.to = initiator.properties["cm:email"];
              mail.parameters.subject = "Публикация статьи";
              mail.parameters.text = "Добрый день.nnВаша статья прошла проверку технического редактора и отправлена на публикацию.";
              mail.execute(bpm_package);
            }
            ]]>
        </script>
      </action>
    </transition>
    <!-- Отклонить статью -->
    <transition name="reject" to="ReviseTask">
      <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
        <!-- Отправляем уведомление -->
        <script>
          <![CDATA[
            logger.log("PublishPaperProcess: Rejected2");
            if (tcwf_notifyMe)
            {
              var mail = actions.create("mail");
              mail.parameters.to = initiator.properties["cm:email"];
              mail.parameters.subject = "Публикация статьи";
              mail.parameters.text = "Добрый день.nnВаша статья не прошла проверку технического редактора и нуждается в доработке. nnКомментарии:n"+ tcwf_comments;
              mail.execute(bpm_package);
            }
            ]]>
        </script>
      </action>
    </transition>
  </task-node>
 
  <!-- Доработка статьи -->
  <task-node name="ReviseTask">
    <!-- Задача для инициатора -->
    <task name="tcwf:reviseTask" swimlane="initiator" />
    <!-- Подать статьи повторно -->
    <transition name="resubmit" to="Submit" />
    <!-- Отменить процесс публикации статьи -->
    <transition name="cancel" to="End" />
  </task-node>
 
 
  <!-- Публикация статьи -->
  <task-node name="PublishTask">
    <!-- Задача для издателя -->
    <task name="tcwf:publishTask" swimlane="publisher" />
    <!-- Конец процесса -->
    <transition name="done" to="End">
      <action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
        <!-- Отправляем уведомление -->
        <script>
          <![CDATA[
            logger.log("PublishPaperProcess: Published");
            if (tcwf_notifyMe)
            {
              var mail = actions.create("mail");
              mail.parameters.to = initiator.properties["cm:email"];
              mail.parameters.subject = "Публикация статьи";
              mail.parameters.text = "Добрый день.nnВаша статья опубликована.";
              mail.execute(bpm_package);
            }
            ]]>
        </script>
      </action>
    </transition>
  </task-node>
 
  <!-- Последний узел -->
  <end-state name="End"/>
 
</process-definition>
<!-- Конец описания процесса -->

 Список swimlane-ов генерируется следующей командой:

./aconfgen.py -cfrs PublishPaperProcess.xml

 Получим вот такой результат:

<!--'corrector' swimlane-->
<swimlane name="corrector">
  <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
    <actor>#{corrector}</actor>
  </assignment>
</swimlane>
<!--'editor' swimlane-->
<swimlane name="editor">
  <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
    <actor>#{editor}</actor>
  </assignment>
</swimlane>
<!--'initiator' swimlane-->
<swimlane name="initiator"/>
<!--'publisher' swimlane-->
<swimlane name="publisher">
  <assignment class="org.alfresco.repo.workflow.jbpm.AlfrescoAssignment">
    <actor>#{publisher}</actor>
  </assignment>
</swimlane>

 

Теперь генерируем модель процесса при помощи команды

./aconfgen.py -mfcrMdia PublishPaperProcess.xml

Вывод:

<?xml version="1.0" encoding="utf-8"?>
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="tcwf:samplemodel">
  <!--Model metadata-->
  <description>Task model for PublishPaperProcess.xml</description>
  <author>fufler</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="tcwf" uri="https://github.com/fufler/aconfgen/prefix/tcwf"/>
  </namespaces>
  <!--List of types-->
  <types>
    <!--Type for tcwf:submitTask task-->
    <type name="tcwf:submitTask">
      <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>bpm:assignee</aspect>
        <aspect>tcwf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for tcwf:correctTask task-->
    <type name="tcwf:correctTask">
      <parent>bpm:workflowTask</parent>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>tcwf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for tcwf:editTask task-->
    <type name="tcwf:editTask">
      <parent>bpm:workflowTask</parent>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>tcwf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for tcwf:reviseTask task-->
    <type name="tcwf:reviseTask">
      <parent>bpm:workflowTask</parent>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>tcwf:customAspect</aspect>
      </mandatory-aspects>
    </type>
    <!--Type for tcwf:publishTask task-->
    <type name="tcwf:publishTask">
      <parent>bpm:workflowTask</parent>
      <!--overrides default properties values-->
      <overrides>
        <property name="bpm:packageItemActionGroup">
          <default>edit_package_item_actions</default>
        </property>
      </overrides>
      <!--Task mandatory aspects-->
      <mandatory-aspects>
        <aspect>tcwf:customAspect</aspect>
      </mandatory-aspects>
    </type>
  </types>
  <!--Custom aspect definition sample-->
  <aspects>
    <aspect name="tcwf:customAspect">
      <title>Custom aspect sample</title>
      <properties>
        <property name="tcwf:customProperty">
          <type>d:string</type>
          <mandatory>false</mandatory>
          <multiple>false</multiple>
        </property>
      </properties>
    </aspect>
  </aspects>
</model>

 

Теперь создадим конфиг для Share. Для демонстрации работы с пайпами будем генерировать конфиг для Share, имея лишь описание процесса. Команда

 ./aconfgen.py -mMdia PublishPaperProcess.xml | ./aconfgen.py -wlSrfc -n publish_paper_process -

 

выдаст нам следующее:

<?xml version="1.0" encoding="utf-8"?>
<alfresco-config>
  <!--Form config for tcwf:submitTask rendering-->
  <config evaluator="string-compare" condition="jbpm$publish_paper_process">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="bpm:assignee"/>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="info" appearance="" label-id="workflow.set.task.info"/>
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <!--Fields-->
          <field id="bpm:assignee" label-id="label.bpm_assignee" set="other"/>
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
        </appearance>
      </form>
    </forms>
  </config>
  <!--Form config to display workflow info-->
  <config evaluator="task-type" condition="tcwf:submitTask">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="bpm:assignee"/>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <!--Fields-->
          <field id="bpm:assignee" label-id="label.bpm_assignee" set="other"/>
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
        </appearance>
      </form>
    </forms>
  </config>
  <!--Form config for tcwf:correctTask rendering-->
  <config evaluator="task-type" condition="tcwf:correctTask">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
          <show id="transitions"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="info" appearance="" label-id="workflow.set.task.info"/>
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <set id="response" appearance="title" label-id="workflow.set.response"/>
          <!--Fields-->
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
          <field id="transitions" set="response"/>
        </appearance>
      </form>
    </forms>
  </config>
  <!--Form config for tcwf:editTask rendering-->
  <config evaluator="task-type" condition="tcwf:editTask">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
          <show id="transitions"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="info" appearance="" label-id="workflow.set.task.info"/>
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <set id="response" appearance="title" label-id="workflow.set.response"/>
          <!--Fields-->
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
          <field id="transitions" set="response"/>
        </appearance>
      </form>
    </forms>
  </config>
  <!--Form config for tcwf:reviseTask rendering-->
  <config evaluator="task-type" condition="tcwf:reviseTask">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
          <show id="transitions"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="info" appearance="" label-id="workflow.set.task.info"/>
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <set id="response" appearance="title" label-id="workflow.set.response"/>
          <!--Fields-->
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
          <field id="transitions" set="response"/>
        </appearance>
      </form>
    </forms>
  </config>
  <!--Form config for tcwf:publishTask rendering-->
  <config evaluator="task-type" condition="tcwf:publishTask">
    <forms>
      <form>
        <!--List of fields to render-->
        <field-visibility>
          <show id="tcwf:customProperty"/>
          <show id="packageItems"/>
          <show id="transitions"/>
        </field-visibility>
        <!--Fields appearance configuration-->
        <appearance>
          <!--Sets definition-->
          <set id="info" appearance="" label-id="workflow.set.task.info"/>
          <set id="other" appearance="title" label-id="workflow.set.other"/>
          <set id="items" appearance="title" label-id="workflow.set.items"/>
          <set id="response" appearance="title" label-id="workflow.set.response"/>
          <!--Fields-->
          <field id="tcwf:customProperty" label-id="label.tcwf_customProperty" set="other"/>
          <field id="packageItems" set="items"/>
          <field id="transitions" set="response"/>
        </appearance>
      </form>
    </forms>
  </config>
</alfresco-config>

 

Команда

./aconfgen.py -W PublishPaperProcess.xml

выдаст нам заготовку для строк интернационализации бизнес-процесса:

tcwf_publishpaper.workflow.title=
tcwf_publishpaper.workflow.description=
tcwf_publishpaper.task.tcwf_correctTask.title=
tcwf_publishpaper.task.tcwf_correctTask.description=
tcwf_publishpaper.task.tcwf_editTask.title=
tcwf_publishpaper.task.tcwf_editTask.description=
tcwf_publishpaper.task.tcwf_reviseTask.title=
tcwf_publishpaper.task.tcwf_reviseTask.description=
tcwf_publishpaper.task.tcwf_publishTask.title=
tcwf_publishpaper.task.tcwf_publishTask.description=
tcwf_publishpaper.node.CorrectTask.transition.approve.title=
tcwf_publishpaper.node.CorrectTask.transition.approve.description=
tcwf_publishpaper.node.CorrectTask.transition.reject.title=
tcwf_publishpaper.node.CorrectTask.transition.reject.description=
tcwf_publishpaper.node.EditTask.transition.approve.title=
tcwf_publishpaper.node.EditTask.transition.approve.description=
tcwf_publishpaper.node.EditTask.transition.reject.title=
tcwf_publishpaper.node.EditTask.transition.reject.description=
tcwf_publishpaper.node.ReviseTask.transition.resubmit.title=
tcwf_publishpaper.node.ReviseTask.transition.resubmit.description=
tcwf_publishpaper.node.ReviseTask.transition.cancel.title=
tcwf_publishpaper.node.ReviseTask.transition.cancel.description=
tcwf_publishpaper.node.PublishTask.transition.done.title=
tcwf_publishpaper.node.PublishTask.transition.done.description=

 Ну и последняя команда

./aconfgen.py -mMdia PublishPaperProcess.xml | ./aconfgen.py -wlSrfc -n publish_paper_process - | ./aconfgen.py -e -

выдаст строки для интернационализации интерфейса Share:

label.tcwf_customProperty=
label.bpm_assignee=

На этом возможности скрипта пока что исчерпываются. Очень приветствуются предложения по улучшению/расширению функционала скрипта. Багфиксы приветствуются еще больше :)

98

Комментарии

Спасибо!

Отличный скрипт.

Только есть маленькая проблемка: в конфигах для Share вместо evaluator ставится evalutor.

 

 

fufler аватар

 Да, есть такое дело. Правда, в локальной версии уже исправил, а тут забыл :)