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=
На этом возможности скрипта пока что исчерпываются. Очень приветствуются предложения по улучшению/расширению функционала скрипта. Багфиксы приветствуются еще больше :)
Комментарии
01/09/2011 - 20:01
Спасибо!
Отличный скрипт.
Только есть маленькая проблемка: в конфигах для Share вместо evaluator ставится evalutor.
01/09/2011 - 20:09
Да, есть такое дело. Правда, в локальной версии уже исправил, а тут забыл :)