Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
devel:documentation:workflows:dev:workflow [2020/06/25 14:52] – [Sending notifications] svandavdevel:documentation:workflows:dev:workflow [2026/04/09 08:39] (current) kolombom
Line 6: Line 6:
  
 Its advantage lies in integration with Spring framework and it is easy to use in ours devstack. Activiti Platform sets up Rest Api and like that it will be using most of it's functionalities. By running Rest and Activiti Platform on a stand-alone application server, we can communicate even with a non-Java environment. Disadvantage of this solution is lossage of direct Java integration. It is possible to call Spring beans directly from workflow process (by [[http://docs.oracle.com/javaee/6/tutorial/doc/gjddd.html|Expression Language]]), which extends functions workflow and accelerating development. Its advantage lies in integration with Spring framework and it is easy to use in ours devstack. Activiti Platform sets up Rest Api and like that it will be using most of it's functionalities. By running Rest and Activiti Platform on a stand-alone application server, we can communicate even with a non-Java environment. Disadvantage of this solution is lossage of direct Java integration. It is possible to call Spring beans directly from workflow process (by [[http://docs.oracle.com/javaee/6/tutorial/doc/gjddd.html|Expression Language]]), which extends functions workflow and accelerating development.
- 
  
 <note tip> <note tip>
-  * **/api/workflow/definitions/** (Workflow definitions) + 
-  * **/api/workflow/tasks/** (Instance of user's tasks) +  * **/api/workflow/definitions/**  (Workflow definitions) 
-  * **/api/workflow/processes/** (Instance of workflow processes)+  * **/api/workflow/tasks/**  (Instance of user's tasks) 
 +  * **/api/workflow/processes/**  (Instance of workflow processes)
 </note> </note>
  
-=====Design of process===== +===== Design of process ===== 
-Design of workflow process is realized with XML in format BPM 2.0. It is very helpful to use Activiti Designer. It is [[http://docs.alfresco.com/5.2/tasks/wf-install-activiti-designer.html|plugin in IDE Eclipse]]. Activiti Designer is user-friendly and allows creating a process without user intervention into XML code.+ 
 +Design of workflow process is realized with XML in format BPM 2.0. It is very helpful to use Activiti Designer. It is [[http://docs.alfresco.com/5.2/tasks/wf-install-activiti-designer.html|plugin in IDE Eclipse]]. Activiti Designer is user-friendly and allows creating a process without user intervention into XML code. In order to be possible to install the Activiti Designer into Eclipse, add its repository address ''[[https://www.activiti.org/designer/update/|https://www.activiti.org/designer/update/]]'' in the Eclipse Install New Software section. A manual could be found [[https://www.websparrow.org/misc/how-to-install-activiti-designer-plugin-in-eclipse-sts|here]].
  
 **Sample of Activiti process in IDE Eclipse plugin:** **Sample of Activiti process in IDE Eclipse plugin:**
  
-{{ :navrh:workflow:workflowexample.png |}}+{{  :navrh:workflow:workflowexample.png  }}
  
-=====Example of usage Expression Language=====+===== Example of usage Expression Language =====
  
-In our example of EL (lower), there is an option to assembly description of user's task. There is element "documentation" in the middle of description in form **${defaultIdmRoleService.get(roleIdentifier).name}**. **${...}** is the definition of the expression, **defaultIdmRoleService** is the name of implementation service (Spring bean). Of this service method **get** is called with parameter id of requested role from process variable **roleIdentifier**. Method returns an instance of the requested role and we used attribute name. The result of this expression is a name of role.+In our example of EL (lower), there is an option to assembly description of user's task. There is element "documentation" in the middle of description in form **${defaultIdmRoleService.get(roleIdentifier).name}**. **${}**  is the definition of the expression, **defaultIdmRoleService**  is the name of implementation service (Spring bean). Of this service method **get**  is called with parameter id of requested role from process variable **roleIdentifier**. Method returns an instance of the requested role and we used attribute name. The result of this expression is a name of role.
  
 Because of this procedure, we need in process attributes identifiers. With identifiers and EL expressions, we can get any necessary data. Because of this procedure, we need in process attributes identifiers. With identifiers and EL expressions, we can get any necessary data.
- 
 <code> <code>
 +
 <userTask id="managerTask1" name="Schválení vedoucím" <userTask id="managerTask1" name="Schválení vedoucím"
- activiti:candidateUsers="admin,user1">+    activiti:candidateUsers="admin,user1">
   <documentation>Schválení přiřazení role   <documentation>Schválení přiřazení role
     ${defaultIdmRoleService.get(roleIdentifier).name} vedoucím pro uživatele     ${defaultIdmRoleService.get(roleIdentifier).name} vedoucím pro uživatele
Line 35: Line 36:
   </documentation>   </documentation>
 </userTask> </userTask>
 +
 </code> </code>
  
-=====User task===== +===== User task ===== 
-Workflow activity **UserTask** can assign a task to a specific user by attribute **assignee**. The task can have just one assignee. If we want to assign more users, who can solve the task, we use attribute **candidateUsers**.+ 
 +Workflow activity **UserTask**  can assign a task to a specific user by attribute **assignee**. The task can have just one assignee. If we want to assign more users, who can solve the task, we use attribute **candidateUsers**.
  
 In this step there are no differences between assignee and candidateUsers. If a user is assignee or is in a list of **candidateUsers**, the user can solve this task. In this step there are no differences between assignee and candidateUsers. If a user is assignee or is in a list of **candidateUsers**, the user can solve this task.
  
-=====Service task=====+===== Service task ===== 
 Service task is activity run automatically by the system. Code, which will be executed, can be defined: Service task is activity run automatically by the system. Code, which will be executed, can be defined:
 +
   * In Java class, which path is defined in attribute **class**.   * In Java class, which path is defined in attribute **class**.
   * In Java class implementing JavaDelegate.   * In Java class implementing JavaDelegate.
   * **By Experssion Language**.   * **By Experssion Language**.
 +In our example of EL (lower) is definition of service activity, which run expression **#{defaultIdmIdentityService.addRole(newIdmIdentityRole, false)}**.
  
-In our example of EL (lower) is definition of service activity, which run expression **#{defaultIdmIdentityService.addRole(newIdmIdentityRole, false)}**.  +This expression calls method **addRole**  for adding a new role. The new role will be passed to **newIdmIdentityRole**  process, which contains new link DTO between role and user. Of service **defaultIdmIdentityService**  method **addRole**  is called from Spring context.
- +
-This expression calls method **addRole** for adding a new role. The new role will be passed to **newIdmIdentityRole** process, which contains new link DTO between role and user. +
-Of service **defaultIdmIdentityService** method **addRole** is called from Spring context. +
-   +
  
 <code> <code>
Line 60: Line 62:
  activiti:expression="#{defaultIdmIdentityService.addRole(newIdmIdentityRole, false)}">  activiti:expression="#{defaultIdmIdentityService.addRole(newIdmIdentityRole, false)}">
 </serviceTask> </serviceTask>
 +
 </code> </code>
  
-=====Script task=====+===== Script task ===== 
 Script task activity allows run scripts defined in the workflow. Workflow supports JavaScript and Groovy. We will be using Groovy for it's performance and specific implementation version (in pom.xml). JavaScript engine is part of Java and implementation version will depend on the version on which version whole application will run. Script task activity allows run scripts defined in the workflow. Workflow supports JavaScript and Groovy. We will be using Groovy for it's performance and specific implementation version (in pom.xml). JavaScript engine is part of Java and implementation version will depend on the version on which version whole application will run.
  
-The script is used, when EL cannot make expression we want. For example of iteration or creation new instance of Object and setting attributes. In our example (lower) we are creating new instance of link object between role and user and object is filled by identificators. This instance is then put in process variables with key **newIdmIdentityRole** execution.setVariable("newIdmIdentityRole", ir)).+The script is used, when EL cannot make expression we want. For example of iteration or creation new instance of Object and setting attributes. In our example (lower) we are creating new instance of link object between role and user and object is filled by identificators. This instance is then put in process variables with key **newIdmIdentityRole**  execution.setVariable("newIdmIdentityRole", ir)).
  
 <code> <code>
-<scriptTask +<scriptTask
  id="scripttaskCreateIdentityRole"  id="scripttaskCreateIdentityRole"
- name="Create IdmIdentityRole"  + name="Create IdmIdentityRole" 
- scriptFormat="groovy" + scriptFormat="groovy"
  activiti:autoStoreVariables="false">  activiti:autoStoreVariables="false">
       <script>import eu.bcvsolutions.idm.core.model.dto.IdmIdentityRoleDto;       <script>import eu.bcvsolutions.idm.core.model.dto.IdmIdentityRoleDto;
          IdmIdentityRoleDto ir = new IdmIdentityRoleDto();          IdmIdentityRoleDto ir = new IdmIdentityRoleDto();
-   ir.setIdentity(identityIdentifier); +      ir.setIdentity(identityIdentifier); 
-   ir.setRole(roleIdentifier); +      ir.setRole(roleIdentifier); 
-   ir.setValidFrom(validFrom); +      ir.setValidFrom(validFrom); 
-   ir.setValidTill(validTill);+      ir.setValidTill(validTill);
          execution.setVariable("newIdmIdentityRole", ir);          execution.setVariable("newIdmIdentityRole", ir);
       </script>       </script>
  </scriptTask>  </scriptTask>
 +
 </code> </code>
-=====Email Task=====+ 
 +===== Email Task ===== 
 Email task activity sends emails. Our emailer (EmailService) is injected into workflow engine, so it is possible disable sending notification (e.g. for debuging). Email can be send to specific identity by putting in username instead of email address. Email task activity sends emails. Our emailer (EmailService) is injected into workflow engine, so it is possible disable sending notification (e.g. for debuging). Email can be send to specific identity by putting in username instead of email address.
  
Line 106: Line 113:
       </extensionElements>       </extensionElements>
     </serviceTask>     </serviceTask>
 +
 </code> </code>
  
-=====Workflow localization=====+===== Workflow localization ===== 
 {{tag> localization}} {{tag> localization}}
  
Line 118: Line 127:
   * **Task name**   * **Task name**
   * **Task description**   * **Task description**
- 
 ==== How translate the name of a workflow process ==== ==== How translate the name of a workflow process ====
  
-For example, we will be using basic process for change the user permission 'approve-identity-change-permissions'. +For example, we will be using basic process for change the user permission 'approve-identity-change-permissions'. This process contains activity with name 'Generating process name'. This activity generates name of a process with included the name of the applicant.
-This process contains activity with name 'Generating process name'. This activity generates name of a process with included the name of the applicant.+
  
 Without translation looks code this: Without translation looks code this:
Line 128: Line 135:
 <code java> <code java>
  Change permissions request for ${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}  Change permissions request for ${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}
 +
 </code> </code>
  
Line 136: Line 144:
 <code java> <code java>
  Change permissions request for {{${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}}}  Change permissions request for {{${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}}}
 +
 </code> </code>
  
Line 148: Line 157:
     }     }
   }   }
 +
 </code> </code>
  
-Where 'approve-identity-change-permissions' is the **ID** of the workflow process definition and 'name' is always **name** of process (persisted in the variable 'processInstanceName'). We are using the variable with the name '0', that is our name of the applicant.+Where 'approve-identity-change-permissions' is the **ID**  of the workflow process definition and 'name' is always **name**  of process (persisted in the variable 'processInstanceName'). We are using the variable with the name '0', that is our name of the applicant.
  
 ==== Translation with the context ==== ==== Translation with the context ====
Line 158: Line 168:
 <code java> <code java>
  Change permissions request for {{${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}}}{{context_secondVariant}}  Change permissions request for {{${identityService.getNiceLabel(identityService.get(applicantIdentifier, null))}}}{{context_secondVariant}}
 +
 </code> </code>
  
Line 167: Line 178:
     }     }
   }   }
 +
 </code> </code>
 +
 ==== How translate the name and description of a workflow task ==== ==== How translate the name and description of a workflow task ====
  
-For example, we will be using again basic process for change the user permission 'approve-identity-change-permissions'. +For example, we will be using again basic process for change the user permission 'approve-identity-change-permissions'. This process contains activity with the name 'Approve by help desk' and ID 'approveByHelpDesk'. This activity creates the approval task for helpdesk department.
-This process contains activity with the name 'Approve by help desk' and ID 'approveByHelpDesk'. This activity creates the approval task for helpdesk department.+
  
 We creates translation in the frontend for this task. In the localization file (usually in the /src/locales/) 'en.json', we have to adds the translation for key '**wf.approve-identity-change-permissions.task.approveByHelpDesk.name**'. We creates translation in the frontend for this task. In the localization file (usually in the /src/locales/) 'en.json', we have to adds the translation for key '**wf.approve-identity-change-permissions.task.approveByHelpDesk.name**'.
Line 186: Line 198:
     }     }
   }   }
 +
 </code> </code>
  
-Where 'approve-identity-change-permissions' is the **ID** of the workflow process definition and under 'task' element is 'approveByHelpDesk', that is **ID** of process task.+Where 'approve-identity-change-permissions' is the **ID**  of the workflow process definition and under 'task' element is 'approveByHelpDesk', that is **ID**  of process task.
  
 Usually we want translate documentation for process task. In our example contains documentation of the 'approveByHelpDesk' task this code: Usually we want translate documentation for process task. In our example contains documentation of the 'approveByHelpDesk' task this code:
Line 194: Line 207:
 <code javascript> <code javascript>
  ${processInstanceName}  ${processInstanceName}
 +
 </code> </code>
  
-It means, documentation contains the name of a process with wrapped variable contains the name of the applicant. +It means, documentation contains the name of a process with wrapped variable contains the name of the applicant. We will uses this variable in translation on the frontend. In the localization file (usually in the /src/locales/) 'en.json', we have to adds the translation for key '**wf.approve-identity-change-permissions.task.approveByHelpDesk.description**'.
-We will uses this variable in translation on the frontend. In the localization file (usually in the /src/locales/) 'en.json', we have to adds the translation for key '**wf.approve-identity-change-permissions.task.approveByHelpDesk.description**'.+
  
 <code javascript> <code javascript>
Line 211: Line 224:
     }     }
   }   }
 +
 </code> </code>
  
 Translation of description contains variable with the name of applicant again. Translation of description contains variable with the name of applicant again.
  
-<note tip>If none transaltion for task in the specific workflow is found, then we try find translation without workflow ID (for example '**wf.task.approveByHelpDesk.description**').</note>  +<note tip>If none transaltion for task in the specific workflow is found, then we try find translation without workflow ID (for example '**wf.task.approveByHelpDesk.description**').</note>
-  +
-=====Passage of workflow - solver decision===== +
-The main principle of the workflow process is a creation of user task. An assigned user must decide how this task will be solved. The development of process is dependent on assign user's decision. We can explain further in an example of process assign role. A user will make a request to assign himself a specific role. A task is created and will be assigned to superior of the requester. Superior will decide if he agrees to assign a role to the requester or not. This **decision** will be **assessed**. In case of rejection, task will be terminated, otherwise, process will continue with next round of decisions.+
  
-It is important to properly define process, so solver will clearly know, of which possibilities he has to choose. And this has to be determined in each user's task. After termination of user's task, evaluation of decision have to be implemented. +===== Passage of workflow - solver decision ===== 
 + 
 +The main principle of the workflow process is a creation of user task. An assigned user must decide how this task will be solved. The development of process is dependent on assign user's decision. We can explain further in an example of process assign role. A user will make a request to assign himself a specific role. A task is created and will be assigned to superior of the requester. Superior will decide if he agrees to assign a role to the requester or not. This **decision**  will be **assessed**. In case of rejection, task will be terminated, otherwise, process will continue with next round of decisions. 
 + 
 +It is important to properly define process, so solver will clearly know, of which possibilities he has to choose. And this has to be determined in each user's task. After termination of user's task, evaluation of decision have to be implemented. 
 + 
 +==== Definition of decisions ====
  
-====Definition of decisions==== 
 Definition of decisions in user tasks have to be more complex than text represented of code enumeration "approve/disapprove". Options of decisions is automatically generated as buttons on detail of task. Each button represent one decision. Definition of decisions in user tasks have to be more complex than text represented of code enumeration "approve/disapprove". Options of decisions is automatically generated as buttons on detail of task. Each button represent one decision.
  
Line 228: Line 244:
  
 Decision attributes: Decision attributes:
-  * **id** - Decision's identificator ("aprove"). 
-  * **label** - Description of decision for user ("Approve", or "core.content.task.decisions.approve"). 
-  * **level** - It sets design of button ("success" button will be green). 
-  * **tooltip** - Description in detail ("By approving you will agree with terms and conditions"). 
-  * **showWarning** - Defines if warning message will  be shown on end of terminating task (true/false). 
-  * **warningMessage** - Defines contend of showWarning message ("Do you really want to approve request?" or "core.content.role.task.approve.warning"). 
-  * **permissions** - Defines required permissions, which user has to have to be able make decision. 
-  * **skipValidation** - If you not used this attribute the behavioral will same as if you set it to false. When you set it to true then the validation of form(userTask) will be ignored.  
  
-===From properties===+  * **id**  - Decision's identificator ("aprove"). 
 +  * **label**  - Description of decision for user ("Approve", or "core.content.task.decisions.approve"). 
 +  * **level**  - It sets design of button ("success" button will be green). 
 +  * **tooltip**  - Description in detail ("By approving you will agree with terms and conditions"). 
 +  * **showWarning**  - Defines if warning message will be shown on end of terminating task (true/false). 
 +  * **warningMessage**  - Defines contend of showWarning message ("Do you really want to approve request?" or "core.content.role.task.approve.warning"). 
 +  * **permissions**  - Defines required permissions, which user has to have to be able make decision. 
 +  * **skipValidation**  - If you not used this attribute the behavioral will same as if you set it to false. When you set it to true then the validation of form(userTask) will be ignored. 
 +  * **reasonRequired **  - This property set to true enforces decision reason to be provided. 
 +=== From properties === 
 We created our decision system, because Aktiviti does not have decision system. On the other hand, Aktiviti allows define **form properties**. This system allows set for each user's activity, which process attributes will be shown. We created our decision system, because Aktiviti does not have decision system. On the other hand, Aktiviti allows define **form properties**. This system allows set for each user's activity, which process attributes will be shown.
  
-In our example (lower) there are two **properties** defined in user task. The first property has id "roleName". It means this task instance gets value of process attribute "roleName". It is also defined if this attribute is readable (**readable** = true), it is not allowed to change (**writable** = false) and it is not required to fill (**required** = false). The second property with id "description" is defined as readable attribute, which can be changed and it is required to be filled.+In our example (lower) there are two **properties**  defined in user task. The first property has id "roleName". It means this task instance gets value of process attribute "roleName". It is also defined if this attribute is readable (**readable**  = true), it is not allowed to change (**writable**  = false) and it is not required to fill (**required**  = false). The second property with id "description" is defined as readable attribute, which can be changed and it is required to be filled.
  
 <code> <code>
     <userTask id="managerTask1" name="Schválení vedoucím" activiti:candidateUsers="admin,tomiska">     <userTask id="managerTask1" name="Schválení vedoucím" activiti:candidateUsers="admin,tomiska">
-      <documentation>Schválení přiřazení role ${defaultIdmRoleService.get(roleIdentifier).name} vedoucím pro uživatele        +      <documentation>Schválení přiřazení role ${defaultIdmRoleService.get(roleIdentifier).name} vedoucím pro uživatele
         ${defaultIdmIdentityService.get(identityIdentifier).getUsername()}.         ${defaultIdmIdentityService.get(identityIdentifier).getUsername()}.
       </documentation>       </documentation>
       <extensionElements>       <extensionElements>
-        <activiti:formProperty +        <activiti:formProperty
           id="roleName"           id="roleName"
           name="Název schvalované role"           name="Název schvalované role"
Line 255: Line 273:
           required="false">           required="false">
         </activiti:formProperty>         </activiti:formProperty>
-        <activiti:formProperty  +        <activiti:formProperty 
-          id="description"  +          id="description" 
-          name="Poznámka žadatele"  +          name="Poznámka žadatele" 
-          type="string"  +          type="string" 
-          readable="true"  +          readable="true" 
-          writable="true" +          writable="true"
           required="true">           required="true">
       </extensionElements>       </extensionElements>
     </userTask>     </userTask>
 +
 </code> </code>
  
-===Decision=== +=== Decision === 
-In previous article we described how **form properties** works. We do not want return complex definition of decison as string, so we need new data typ. Our data type is registered by Spring configuration in Aktiviti engine as **DecisionFormType** with key **decision**.+ 
 +In previous article we described how **form properties**  works. We do not want return complex definition of decison as string, so we need new data typ. Our data type is registered by Spring configuration in Aktiviti engine as **DecisionFormType**  with key **decision**.
  
 Decision values are defined by workflow definition as JSON objects and these values are written at creation of task (**approve**, **disapprove**, **backToManager**). Decision values are defined by workflow definition as JSON objects and these values are written at creation of task (**approve**, **disapprove**, **backToManager**).
Line 274: Line 294:
  <dataObject id="approve" name="approve" itemSubjectRef="xsd:string">  <dataObject id="approve" name="approve" itemSubjectRef="xsd:string">
       <extensionElements>       <extensionElements>
-        <activiti:value>{"label": "Schválit","showWarning":false, "warningMessage":"Opravdu chcete úkol schválit?",   +        <activiti:value>{"label": "Schválit","showWarning":false, "warningMessage":"Opravdu chcete úkol schválit?",
           "level":"success","tooltip":"Schválit úkol a předat na administrátora"}           "level":"success","tooltip":"Schválit úkol a předat na administrátora"}
         </activiti:value>         </activiti:value>
Line 281: Line 301:
     <dataObject id="disapprove" name="disapprove" itemSubjectRef="xsd:string">     <dataObject id="disapprove" name="disapprove" itemSubjectRef="xsd:string">
       <extensionElements>       <extensionElements>
-        <activiti:value>{"label": "Zamítnout","showWarning":true, "warningMessage":"Opravdu chcete žádost zamítnout?", +        <activiti:value>{"label": "Zamítnout","showWarning":true, "warningMessage":"Opravdu chcete žádost zamítnout?",
           "level":"danger","tooltip":"Zamítnout úkolu"}           "level":"danger","tooltip":"Zamítnout úkolu"}
         </activiti:value>         </activiti:value>
Line 288: Line 308:
     <dataObject id="backToManager" name="backToManager" itemSubjectRef="xsd:string">     <dataObject id="backToManager" name="backToManager" itemSubjectRef="xsd:string">
       <extensionElements>       <extensionElements>
-        <activiti:value>{"label": "Vrátit vedoucímu","showWarning":true, "warningMessage":"Opravdu chcete žádost vrátit +        <activiti:value>{"label": "Vrátit vedoucímu","showWarning":true, "warningMessage":"Opravdu chcete žádost vrátit
           vedoucímu?", "level":"warning","tooltip":"Vrátit úkol vedoucímu"}           vedoucímu?", "level":"warning","tooltip":"Vrátit úkol vedoucímu"}
         </activiti:value>         </activiti:value>
       </extensionElements>       </extensionElements>
     </dataObject>     </dataObject>
-</code> 
  
 +</code>
  
-Access to these attributes is realized by form properties (type = decision). In our example (lower) there is user's task, which has form properties **approve** and **disapprove**. In this activity user will have two decision options (buttons).+Access to these attributes is realized by form properties (type = decision). In our example (lower) there is user's task, which has form properties **approve**  and **disapprove**. In this activity user will have two decision options (buttons).
  
 <code> <code>
Line 305: Line 325:
       </extensionElements>       </extensionElements>
     </userTask>     </userTask>
-</code> 
  
 +</code>
  
-====Evaluation of decision====+==== Evaluation of decision ====
  
 We already described how to define decision for user's activity, Now we will evaluate decisions in process. We already described how to define decision for user's activity, Now we will evaluate decisions in process.
  
-Before closing user's task, Aktiviti engine is called with form properties values and decision's id (decision which user made). Decision's id will be put as process attribute with **decision** as name.+Before closing user's task, Aktiviti engine is called with form properties values and decision's id (decision which user made). Decision's id will be put as process attribute with **decision**  as name.
  
-In next step component **exclusiveGateway** (XOR) is used. This component evaluate conditions on each outgoing path, if evaluation of path is **true**, process will continue on this path. If all outgoing paths are evaluated as **false**, exception is thrown+In next step component **exclusiveGateway**  (XOR) is used. This component evaluate conditions on each outgoing path, if evaluation of path is **true**, process will continue on this path. If all outgoing paths are evaluated as **false**, exception is thrown.
- +
-In our example (lower) is defined two outgoing paths - **sequenceFlow**. The first path has condition **${decision.equals("disapprove")}**. It means, if **decision** was "approve", then process will continue on this path. The second path is similiar, but has contidion if decision was "disapprove".+
  
 +In our example (lower) is defined two outgoing paths - **sequenceFlow**. The first path has condition **${decision.equals("disapprove")}**. It means, if **decision**  was "approve", then process will continue on this path. The second path is similiar, but has contidion if decision was "disapprove".
  
 <code> <code>
Line 327: Line 346:
       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${decision.equals("disapprove")}]]></conditionExpression>       <conditionExpression xsi:type="tFormalExpression"><![CDATA[${decision.equals("disapprove")}]]></conditionExpression>
     </sequenceFlow>     </sequenceFlow>
 +
 </code> </code>
  
-=====Dynamic detail of task===== +===== Dynamic detail of task ===== 
-We described **Form properties** as process attributes, which will be shown in user's task and we defined **decisions** as buttons for controlling the flow (paths) in process. Form properties can be used for dynamic design for each task.+ 
 +We described **Form properties**  as process attributes, which will be shown in user's task and we defined **decisions**  as buttons for controlling the flow (paths) in process. Form properties can be used for dynamic design for each task.
  
 Principle is similiar to **decision**. In process is definition, which components will be used, and on the frontend side is by that definition generated detail by using default frontend components. Aktiviti engine by default provide a few components (**string**, **enum**, **date**), which can be used for task definition. Principle is similiar to **decision**. In process is definition, which components will be used, and on the frontend side is by that definition generated detail by using default frontend components. Aktiviti engine by default provide a few components (**string**, **enum**, **date**), which can be used for task definition.
Line 336: Line 357:
 But these components are insufficient, it cannot define if string will be shown as one-line field or text editor. Because of this we created our special data type, which can be used in user's task: But these components are insufficient, it cannot define if string will be shown as one-line field or text editor. Because of this we created our special data type, which can be used in user's task:
  
-  * **textArea** - It will be shown as one-line field (as additional options, key "placeholder" can be used). +  * **textArea**  - It will be shown as one-line field (as additional options, key "placeholder" can be used). 
-  * **textField** - It will be shown as multiline editor (as additional options, key "placeholder" can be used).+  * **textField**  - It will be shown as multiline editor (as additional options, key "placeholder" can be used).
   * **date**  - Shows as date component (conversion to "yyyy-MM-dd").   * **date**  - Shows as date component (conversion to "yyyy-MM-dd").
-  * **checkbox** - Shows as checkbox. +  * **checkbox**  - Shows as checkbox. 
-  * **selectBox** - Show as selectbox. It is using EnumSelectBox component. +  * **selectBox**  - Show as selectbox. It is using EnumSelectBox component. 
-  * **taskHistory** - It can show previous tasks from WF in table. +  * **taskHistory**  - It can show previous tasks from WF in table.
 Each component has additional settings **From property**: Each component has additional settings **From property**:
-  * **id** - Unique component identifier in single process. If attribute **variables** is not filled, then id even define process attribute. 
-  * **name** - Name, which will be used as description of component (label). It is possible to use final text or localization key. 
-  * **type** - Type of component. See above (textArea, textField, etc.). 
-  * **variable** - Defines process attribute, which can be used in component (value can be read, but also result can be written). 
-  * **expression** - Expression (EL) can be defined here. Expression will be executed at start of the task and result will be used as value of component. 
-  * **readable** - Defines, if component will be shown (default value is **true**). 
-  * **writable** - Defines, if component could be edited (default value is **true**). 
-  * **required** - Defines, if value will be mandatory (default value is **false**). 
  
-<note tip>Component definitions order in **Form properties** are the same as order components after frontend generating.</note>+  * **id**  - Unique component identifier in single process. If attribute **variables**  is not filled, then id even define process attribute. 
 +  * **name**  - Name, which will be used as description of component (label). It is possible to use final text or localization key. 
 +  * **type**  - Type of component. See above (textArea, textField, etc.). 
 +  * **variable**  - Defines process attribute, which can be used in component (value can be read, but also result can be written). 
 +  * **expression**  - Expression (EL) can be defined here. Expression will be executed at start of the task and result will be used as value of component. 
 +  * **readable**  - Defines, if component will be shown (default value is **true**). 
 +  * **writable**  - Defines, if component could be edited (default value is **true**). 
 +  * **required**  - Defines, if value will be mandatory (default value is **false**). 
 +<note tip>Component definitions order in **Form properties**  are the same as order components after frontend generating.</note>
  
-===Example of user's task selectBox:=== +=== Example of user's task selectBox: ===
-{{:devel:documentation:workflows:dev:selectbox.png|}}+
  
-Data which are displayed in selectBox can be set via Map<String, String> where value is the text which will be displayed in GUI. You can set localization key into value. +{{.:selectbox.png}}
-You can set options directly in WF without service on BE. Just use field Default in Form property configuration {"option1":"label for 1","option2":"label for 2"or see XML example lower+
  
-===Example of user's task taskHistory:=== +Data which are displayed in selectBox can be set via Map<String, String> where value is the text which will be displayed in GUI. You can set localization key into value. You can set options directly in WF without service on BE. Just use field Default in Form property configuration {"option1":"label for 1","option2":"label for 2"or see XML example lower
-{{:devel:documentation:workflows:dev:history.png|}}+
  
-Data which are displayed in table can be set via List<WorkflowHistoricTaskInstanceDto>+=== Example of user's task taskHistory: ===
  
 +{{.:history.png}}
  
 +Data which are displayed in table can be set via List<WorkflowHistoricTaskInstanceDto>
  
 In our user's task example (lower) there are generated frontend result and definition in XML. In our user's task example (lower) there are generated frontend result and definition in XML.
  
-===Example of user's task with dynamic detail:=== +=== Example of user's task with dynamic detail: === 
-{{ :navrh:workflow:detaildynamic.png |}} + 
-===Example of user's task definition with dynamic detail:===+{{  :navrh:workflow:detaildynamic.png  }} 
 + 
 +=== Example of user's task definition with dynamic detail: ===
  
 <code> <code>
-  <userTask id="approveTask" name="Schválení uživatelem s rolí &quot;SuperAdminRole&quot;" activiti:candidateUsers="#{defaultIdmIdentityService.findAllByRole(defaultIdmRoleService.getByName(&quot;superAdminRole&quot;).getId())}">+  <userTask id="approveTask" name="Schválení uživatelem s rolí "SuperAdminRole"" activiti:candidateUsers="#{defaultIdmIdentityService.findAllByRole(defaultIdmRoleService.getByName("superAdminRole").getId())}">
       <documentation>Schválení přiřazení role ${defaultIdmRoleService.get(roleIdentifier).name} pro uživatele ${applicantUsername}.</documentation>       <documentation>Schválení přiřazení role ${defaultIdmRoleService.get(roleIdentifier).name} pro uživatele ${applicantUsername}.</documentation>
       <extensionElements>       <extensionElements>
Line 388: Line 409:
           <activiti:value id="tooltip" name="Platnost přiřazení role"></activiti:value>           <activiti:value id="tooltip" name="Platnost přiřazení role"></activiti:value>
         </activiti:formProperty>         </activiti:formProperty>
-        <activiti:formProperty id="options" type="selectBox" default="{&quot;option1&quot;:&quot;label for 1&quot;,&quot;option2&quot;:&quot;label for 2&quot;}"></activiti:formProperty>+        <activiti:formProperty id="options" type="selectBox" default="{"option1":"label for 1","option2":"label for 2"}"></activiti:formProperty>
         <activiti:formProperty id="history" type="taskHistory"></activiti:formProperty>         <activiti:formProperty id="history" type="taskHistory"></activiti:formProperty>
       </extensionElements>       </extensionElements>
     </userTask>     </userTask>
 +
 </code> </code>
-=====Custom task detail=====+ 
 +=== Setting solver note message - decision reason === 
 + 
 +The task solver is able to set a reason of the decision or any other note to the solved task. The text area serving to this purpose is part of the confirmation modal window which appears, when user clicks the decision button. Display of this modal window (see in the picture below) has to be enabled by ''decision''  property ''showWarning''  or ''reasonRequired''  in particular workflow. When ''reasonRequired''  is used the solver note/decision reason is **mandatory**. More about setting of this feature is in the following configuration paragraph. 
 + 
 +{{  .:solverreasonmodal.png  }} 
 + 
 +When the task is solved the solver's reason can be seen in two places. The first one is in the table of tasks which are part of given workflow. And the second one in the detail of the particular task. 
 + 
 +{{.:worflowresultoverview.png?680| }}{{  .:decisiontaskdetail.png?680}} 
 + 
 +**Configuration** 
 + 
 +There exist several ways how to configure solver notes. The general approach is the setting from workflow. Workflow bpmn20 file contains the following section which is used for configuration of decision buttons in userTask. The section looks like this: 
 +<code> 
 + 
 +<dataObject id="approve" name="approve" itemSubjectRef="xsd:string"> 
 +  <extensionElements> 
 +    <activiti:value>{"showWarning":false,"level":"success","reasonRequired":true}</activiti:value> 
 +  </extensionElements> 
 +</dataObject> 
 + 
 +</code> 
 + 
 +<note tip> To avoid the need of workflow modification and an easy configuration of the most common decisions i.e. ''approve''  and ''disapprove''  there are shortcuts in IdM properties. Both accepts **true**  or **false**  values. 
 + 
 +''idm.sec.core.wf.approval.reason.required''  - sets decision reason required for APPROVAL decision ''idm.sec.core.wf.disapproval.reason.required''  - sets decision reason required for DISAPPROVAL decision </note> 
 + 
 +If decision button ''apporve''  is set like shown above, ''showWarning''  attribute ensures displaying of the modal window, where a solver note can be inserted. The second parameter ''reasonRequired''  specifies that the solver note is mandatory and the action cannot be finished without it. If only ''showWarning''  is set the note can be set but its filling is not required. It's important to note that when our workflow calls other sub-workflows it is important and also sufficient to change this setting in the parent workflow only. The setting is propagate from the parent WF to sub-WF and will override the setting of the sub-WF. In most cases the userTask uses two decision types: ''id=approve''  and ''id=disapporve''. One can avoid the need of workflow changing for those two buttons thanks to the IdM properties ''idm.sec.core.wf.approval.reason.required''  and ''idm.sec.core.wf.disapproval.reason.required''. If they are set to true solver note/reason is required for the corresponding type of decision button choice. The setting works globally for all workflows in the IdM. If those IdM properties are set and there is also set ''reasonRequired''  parameter in a workflow, the setting from the workflow is preferred over that IdM properties for userTasks from that workflow. 
 + 
 +===== Custom task detail =====
  
 Dynamic definition of components provide us with quick and easy way to add another item to task. But even this system has a few disadvantages: Dynamic definition of components provide us with quick and easy way to add another item to task. But even this system has a few disadvantages:
  
- * Components cannot interact with each other. +* Components cannot interact with each other. * Cannot modified placement of each component on task detail (except for order). * Cannot show complex components like tables with specific properties (show detail, specific filtr, etc.).
- * Cannot modified placement of each component on task detail (except for order). +
- * Cannot show complex components like tables with specific properties (show detail, specific filtr, etc.).+
  
 But if some of these feature is necessary on task detail, yet it can be made by specially created detail, which can be modified as you wish. it means, in frontend will be created new component (page), which will be used in specific task. But if some of these feature is necessary on task detail, yet it can be made by specially created detail, which can be modified as you wish. it means, in frontend will be created new component (page), which will be used in specific task.
  
-Usage of this specially created detail is setted in user's task detail with parameter **Form key**. If this parameter is filled, **DynamicTaskDetail** will not be used, but component with same name as value of Form key parameter will be used.+Usage of this specially created detail is setted in user's task detail with parameter **Form key**. If this parameter is filled, **DynamicTaskDetail**  will not be used, but component with same name as value of Form key parameter will be used.
  
 <code> <code>
     <userTask id="managerTask" name="Schválení žádosti vedoucím" activiti:formKey="dynamicRoleTaskDetail">     <userTask id="managerTask" name="Schválení žádosti vedoucím" activiti:formKey="dynamicRoleTaskDetail">
 +
 </code> </code>
- 
  
 <note tip>Definition of link between name of component and it's real reprezentation (import) is determined in **component-descriptor.js**:</note> <note tip>Definition of link between name of component and it's real reprezentation (import) is determined in **component-descriptor.js**:</note>
Line 424: Line 474:
   ]   ]
 }; };
 +
 </code> </code>
  
- +===== Localization =====
-=====Localization=====+
  
 There are three ways to name buttons in decision: There are three ways to name buttons in decision:
-  * **Custom text** - If we do not want to create localization we can put in custom text. We could put "Solve" as **label**, and because for that value in localization will not be found result, value "Solve" will be written. 
-  * **Localization key** - If we want use specific localization translation, localization key have to be used. In **label** will be put something like "decision.approve.label". In this case value from localization for this key will be used. 
-  * **Automatic assembly of localization key** - If we do not fill value **label**, this value will be filled automatically in form **decision.approve.label**, where approve is id of item. For this key system will search in localization and if key will not be found, key will be shown. 
  
 +  * **Custom text**  - If we do not want to create localization we can put in custom text. We could put "Solve" as **label**, and because for that value in localization will not be found result, value "Solve" will be written.
 +  * **Localization key**  - If we want use specific localization translation, localization key have to be used. In **label**  will be put something like "decision.approve.label". In this case value from localization for this key will be used.
 +  * **Automatic assembly of localization key**  - If we do not fill value **label**, this value will be filled automatically in form **decision.approve.label**, where approve is id of item. For this key system will search in localization and if key will not be found, key will be shown.
 <note>Localization is supported for these decision's items **label**, **tooltip**, **warningMessage**.</note> <note>Localization is supported for these decision's items **label**, **tooltip**, **warningMessage**.</note>
  
-<note tip>In similar way works localization for items in dynamic generated detail task. Automatic key uses prefix **formData**. Supported items are **name**, **tooltip**, **placeholder** </note>+<note tip>In similar way works localization for items in dynamic generated detail task. Automatic key uses prefix **formData**. Supported items are **name**, **tooltip**, **placeholder**  </note>
  
 **Localization example** **Localization example**
 +
 <code> <code>
  "decision" : {  "decision" : {
Line 484: Line 535:
     }     }
   }   }
 +
 </code> </code>
  
-=====Sending notifications=====+===== Sending notifications ===== 
 There is global configuration in application properties, which can turned on or off sending notifications from UserTask. There is global configuration in application properties, which can turned on or off sending notifications from UserTask.
 +
 <code> <code>
 # Global property that allow disable or enable sending notification from WF # Global property that allow disable or enable sending notification from WF
 idm.sec.core.wf.notification.send=false idm.sec.core.wf.notification.send=false
 +
 </code> </code>
  
 Sending notification can be turnd off in each UserTask by setting Form property **sendNotification**. Example of this setting: Sending notification can be turnd off in each UserTask by setting Form property **sendNotification**. Example of this setting:
 +
 <code xml> <code xml>
 <activiti:formProperty <activiti:formProperty
Line 501: Line 557:
     writable="false">     writable="false">
 </activiti:formProperty> </activiti:formProperty>
 +
 </code> </code>
  
-Type of variable have to be **configuration**. It is FormType, which is not propagated to frontend. ID or name have to be **sendNotification**. It does not matter even in case of **idm.sec.core.wf.notification.send** is setted to false, because form property **sendNotification** has **higher priority**.+Type of variable have to be **configuration**. It is FormType, which is not propagated to frontend. ID or name have to be **sendNotification**. It does not matter even in case of **idm.sec.core.wf.notification.send**  is setted to false, because form property **sendNotification**  has **higher priority**.
  
-If Form property **sendNotification** is not explicitly filled, notification will be sent.+If Form property **sendNotification**  is not explicitly filled, notification will be sent.
  
-New listener **TaskSendNotificationEventListener** was created for purposes of sending notifications. This listener reacts on these events: TASK\_ASSIGNED, TASK\_COMPLETED (comes later) and TASK\_CREATED. Each event sends notification with its own topic. Recipient is picked from TaskEntity from assignee attribute. In case of assignee is not filled, notification is send to all candidates. Before notification is sent, existence of identity is verified.+New listener **TaskSendNotificationEventListener**  was created for purposes of sending notifications. This listener reacts on these events: TASK\_ASSIGNED, TASK\_COMPLETED (comes later) and TASK\_CREATED. Each event sends notification with its own topic. Recipient is picked from TaskEntity from assignee attribute. In case of assignee is not filled, notification is send to all candidates. Before notification is sent, existence of identity is verified.
  
  
  • by svandav