From 3aa5f36ced7549cf11122c95f699e28387031dcc Mon Sep 17 00:00:00 2001
From: "S.Listl" <S.Listl@SLISTL.aditosoftware.local>
Date: Fri, 6 Mar 2020 16:00:22 +0100
Subject: [PATCH] Webservices for flowable-modeler

---
 .../recordcontainers/jdito/contentProcess.js  |   6 +-
 .../_____LANGUAGE_EXTRA.aod                   |   3 +
 .../_____LANGUAGE_de/_____LANGUAGE_de.aod     |   7 +-
 .../_____LANGUAGE_en/_____LANGUAGE_en.aod     |   3 +
 process/Sql_lib/documentation.adoc            |  18 +++
 process/workflowPrivileges_rest/process.js    |  70 +++++++++++
 .../workflowPrivileges_rest.aod               |  14 +++
 process/workflowRoles_rest/process.js         |  81 ++++++++++++
 .../workflowRoles_rest/workflowRoles_rest.aod |  14 +++
 process/workflowUsers_rest/process.js         | 118 ++++++++++++++----
 .../workflowUsers_rest/workflowUsers_rest.aod |   3 +-
 role/PROJECT_Workflow/PROJECT_Workflow.aod    |   6 +
 12 files changed, 312 insertions(+), 31 deletions(-)
 create mode 100644 process/workflowPrivileges_rest/process.js
 create mode 100644 process/workflowPrivileges_rest/workflowPrivileges_rest.aod
 create mode 100644 process/workflowRoles_rest/process.js
 create mode 100644 process/workflowRoles_rest/workflowRoles_rest.aod
 create mode 100644 role/PROJECT_Workflow/PROJECT_Workflow.aod

diff --git a/entity/WorkflowTask_entity/recordcontainers/jdito/contentProcess.js b/entity/WorkflowTask_entity/recordcontainers/jdito/contentProcess.js
index 44b94eb5a22..50cb3e1f446 100644
--- a/entity/WorkflowTask_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/WorkflowTask_entity/recordcontainers/jdito/contentProcess.js
@@ -34,15 +34,15 @@ result.object((function ()
     }
     else
     {
-    //    if (isOnlyForCurrentUser)
-    //        loadConfig.candidateIdentifier(EmployeeUtils.getCurrentUserId());
+        if (isOnlyForCurrentUser)
+            loadConfig.candidateIdentifier(EmployeeUtils.getCurrentUserId());
         if (processInstanceId)
             loadConfig.processInstanceId(processInstanceId);
     }
     
     var tasks = JSON.parse(onlyFinished
         ? workflow.getHistoricTasks(loadConfig)
-        : workflow.getTasks(loadConfig));
+        : workflow.getTasks(loadConfig)) || [];
     
     //it is possible that a task is requested that was just finished -> if a task with given id could not be found, try to load it from historic tasks
     if (tasks.length === 0 && taskIds && taskIds.length > 0 && !onlyFinished)
diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
index 38cd6d2cb61..104cbcd35be 100644
--- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
+++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
@@ -6095,6 +6095,9 @@
     <entry>
       <key>Resume</key>
     </entry>
+    <entry>
+      <key>Finished tasks</key>
+    </entry>
   </keyValueMap>
   <font name="Dialog" style="0" size="11" />
   <sqlModels>
diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index cb56cd502ce..88d7a085aa1 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -2131,7 +2131,7 @@
     </entry>
     <entry>
       <key>Workflow instances</key>
-      <value>Vorgangsinstanzen</value>
+      <value>Prozessinstanzen</value>
     </entry>
     <entry>
       <key>{$TASK_PRIORITY_NONE}</key>
@@ -6921,7 +6921,7 @@
     </entry>
     <entry>
       <key>Workflow instance</key>
-      <value>Vorgangsinstanz</value>
+      <value>Prozessinstanz</value>
     </entry>
     <entry>
       <key>Source duplicate</key>
@@ -7723,7 +7723,7 @@ Bitte Datumseingabe prüfen</value>
     </entry>
     <entry>
       <key>Workflow</key>
-      <value>Vorgang</value>
+      <value>Prozess</value>
     </entry>
     <entry>
       <key>Campaign steps </key>
@@ -7802,6 +7802,7 @@ Bitte Datumseingabe prüfen</value>
     </entry>
     <entry>
       <key>Workflow definitions</key>
+      <value>Prozessdefinitionen</value>
     </entry>
     <entry>
       <key>Version</key>
diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
index a81de862077..8c69eed791a 100644
--- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
+++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
@@ -6153,6 +6153,9 @@
     <entry>
       <key>Resume</key>
     </entry>
+    <entry>
+      <key>Finished tasks</key>
+    </entry>
   </keyValueMap>
   <font name="Dialog" style="0" size="11" />
 </language>
diff --git a/process/Sql_lib/documentation.adoc b/process/Sql_lib/documentation.adoc
index 2017f4b9646..33dc30f6edc 100644
--- a/process/Sql_lib/documentation.adoc
+++ b/process/Sql_lib/documentation.adoc
@@ -262,6 +262,24 @@ var costData = newSelect("CAMPAIGNCOSTID, CAMPAIGNSTEP_ID, CAMPAIGNSTEP.NAME, CA
                     .table();
 ----
 
+=== apply
+
+WARNING: Not all databases support "outer apply" or "cross apply".
+
+An apply can also be added via `.join`. If you set the parameter `pReplacementForWordJoin`, the given keyword will be used instead of "join". 
+
+Here's an example for the usage of "outer apply":
+[source,js]
+----
+var activitySelect = newSelect("SUBJECT, INFO, personLink.OBJECT_ROWID")
+    .from("ACTIVITY")
+    .join(
+        newSelect("OBJECT_ROWID")
+            .from("ACTIVITYLINK")
+            .where("ACTIVITYLINK.OBJECT_TYPE", "Person"),
+        null, "personLink", "outer", "apply"); //it would also be possible to omit pPrefix and then write "outer apply"
+----
+
 === group by
 
 `groupBy(pFields)` adds a group by statement to the SQL code. The parameter can be filled the same way as `.select(pFields)`.
diff --git a/process/workflowPrivileges_rest/process.js b/process/workflowPrivileges_rest/process.js
new file mode 100644
index 00000000000..b6efb5d9f06
--- /dev/null
+++ b/process/workflowPrivileges_rest/process.js
@@ -0,0 +1,70 @@
+import("system.text");
+import("system.tools");
+
+function restget (pRequest)
+{
+    let request = JSON.parse(pRequest);
+    let header = request.header;
+    let privFilter = header.Privfilter
+        ? JSON.parse(header.Privfilter)
+        : {};
+    
+    let privileges = {
+        ACCESS_IDM : {
+            id : "access-idm-f8-4e7f-85ac-57ae095925ec",
+            name : "access-idm"
+        },
+        ACCESS_MODELER : {
+            id : "access-modeler-50e-a22d-b9acb75047d7",
+            name : "access-modeler"
+        },
+        ACCESS_ADMIN : {
+            id : "access-admin-346a6-b871-bc5f7be120a2",
+            name : "access-admin"
+        },
+        ACCESS_TASK : {
+            id : "access-task-9-4398-a1b5-3ae642de2169",
+            name : "access-task"
+        },
+        ACCESS_REST_API : {
+            id : "access-rest-api-0b-990a-f1b27506ed80",
+            name : "access-rest-api"
+        }
+    };
+    let privilegeArray = [privileges.ACCESS_IDM, privileges.ACCESS_MODELER, privileges.ACCESS_ADMIN, privileges.ACCESS_TASK, privileges.ACCESS_REST_API];
+    if (privFilter.id)
+    {
+        let privilege = privilegeArray.find(function (priv)
+        {
+            return priv.id == privFilter.id;
+        });
+        privilegeArray = privilege ? [privilege] : [];
+    }
+    if (privFilter.name)
+    {
+        let privilege = privilegeArray.find(function (priv)
+        {
+            return priv.name == privFilter.name;
+        });
+        privilegeArray = privilege ? [privilege] : [];
+    }
+    
+    let roles;
+    if (privFilter.userId)
+    {
+        let user = tools.getUserByAttribute(tools.NAME, privFilter.userId);
+        roles = user ? user[tools.ROLENAMES].slice() : [];
+    }
+    else if (privFilter.groupId)
+        roles = [privFilter.groupId];
+    else
+        roles = privFilter.groupIds;
+    
+    let filteredPrivileges = [];
+    if (!roles || roles.indexOf("PROJECT_Workflow") !== -1 || roles.indexOf("INTERNAL_ADMINISTRATOR") !== -1)
+        filteredPrivileges = privilegeArray;
+    
+    request.response.body = JSON.stringify(filteredPrivileges);
+
+    return JSON.stringify(request);
+}
diff --git a/process/workflowPrivileges_rest/workflowPrivileges_rest.aod b/process/workflowPrivileges_rest/workflowPrivileges_rest.aod
new file mode 100644
index 00000000000..5a75e4758d9
--- /dev/null
+++ b/process/workflowPrivileges_rest/workflowPrivileges_rest.aod
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1">
+  <name>workflowPrivileges_rest</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <process>%aditoprj%/process/workflowPrivileges_rest/process.js</process>
+  <publishAsWebservice v="false" />
+  <style>REST</style>
+  <restAcceptedMimeType>application/json</restAcceptedMimeType>
+  <restDeliveredMimeType>application/json</restDeliveredMimeType>
+  <jditoWebserviceUser>flowableIdmService</jditoWebserviceUser>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/workflowRoles_rest/process.js b/process/workflowRoles_rest/process.js
new file mode 100644
index 00000000000..07b379e4fd8
--- /dev/null
+++ b/process/workflowRoles_rest/process.js
@@ -0,0 +1,81 @@
+import("system.text");
+import("system.tools");
+
+function restget (pRequest)
+{
+    let request = JSON.parse(pRequest);
+    let header = request.header;
+    let roleIds = header.Ids && JSON.parse(header.Ids);
+    let userId = header.Userid;
+    
+    if (userId)
+    {
+        let user = tools.getUserByAttribute(tools.NAME, userId);
+        roleIds = user ? user[tools.ROLENAMES].slice() : [];
+    }
+    
+    let roles = tools.getAllRoles([tools.ROLE_PROJECT, tools.ROLE_CUSTOM], false);
+    roles = Object.keys(roles).map(function (role)
+    {
+        let [title, type, desc, id] = roles[role];
+        return {
+            name : title,
+            id : id,
+            type : type
+        };
+    });
+    
+    let filter = {
+        EQUAL : "equal",
+        IGNORECASE : "ignoreCase",
+        IN : "in",
+        LIKE : "like",
+        LIKEIGNORECASE : "likeIgnoreCase",
+        
+        _filters : [],
+        addIfSet : function (pUserPropertyName, pFilterValue, pType)
+        {
+            if (pFilterValue && pFilterValue.length !== 0)
+                this.add(pUserPropertyName, pFilterValue, pType);
+        },
+        add : function (pUserPropertyName, pFilterValue, pType)
+        {
+            this._filters.push([pUserPropertyName, pFilterValue, pType]);
+        },
+        check : function  (pUser)
+        {
+            return this._filters.every(function ([prop, value, type])
+            {
+                let userValue = pUser[prop] || "";
+                let flags;
+                switch (type)
+                {
+                    case this.EQUAL:
+                        return userValue == value;
+                    case this.IGNORECASE:
+                        return userValue.toUpperCase() == value.toUpperCase();
+                    case this.IN:
+                        return value.indexOf(userValue) !== -1;
+                    case this.LIKEIGNORECASE:
+                        flags = "i";
+                    case this.LIKE:
+                        value = text.replaceAll(value, {"%" : ".*", "_" : "."});
+                        return new RegExp(value, flags).test(userValue);
+                }
+                return false;
+            }, this);
+        }
+    };
+    
+    filter.addIfSet("id", header.Id, filter.EQUAL);
+    filter.addIfSet("id", roleIds, filter.IN);
+    filter.addIfSet("name", header.Name, filter.EQUAL);
+    filter.addIfSet("name", header.Namelike, filter.LIKE);
+    filter.addIfSet("name", header.Namelikeignorecase, filter.LIKEIGNORECASE);
+    
+    roles = roles.filter(filter.check, filter);
+    
+    request.response.body = JSON.stringify(roles);
+
+    return JSON.stringify(request);
+}
diff --git a/process/workflowRoles_rest/workflowRoles_rest.aod b/process/workflowRoles_rest/workflowRoles_rest.aod
new file mode 100644
index 00000000000..7bdd09bfb46
--- /dev/null
+++ b/process/workflowRoles_rest/workflowRoles_rest.aod
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1">
+  <name>workflowRoles_rest</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <process>%aditoprj%/process/workflowRoles_rest/process.js</process>
+  <publishAsWebservice v="false" />
+  <style>REST</style>
+  <restAcceptedMimeType>application/json</restAcceptedMimeType>
+  <restDeliveredMimeType>application/json</restDeliveredMimeType>
+  <jditoWebserviceUser>flowableIdmService</jditoWebserviceUser>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/workflowUsers_rest/process.js b/process/workflowUsers_rest/process.js
index 80446eb27dc..6f33fd3573c 100644
--- a/process/workflowUsers_rest/process.js
+++ b/process/workflowUsers_rest/process.js
@@ -1,3 +1,4 @@
+import("system.text");
 import("system.vars");
 import("system.logging");
 import("system.tools");
@@ -10,19 +11,90 @@ function restget (pRequest)
     const MIN_SEARCHLENGTH = 0;
 
     let request = JSON.parse(pRequest);
-    let searchValue = request.header.filter || request.header.Filter;
-
-    request.response.body = JSON.stringify({users : _getUsers(searchValue)});
+    let header = request.header;
+    let userFilter = header.Userfilter 
+        ? JSON.parse(header.Userfilter)
+        : {};
+    
+    //object that helps with filtering, it provides functions for adding conditions to the object and for checking the conditions
+    let filter = {
+        EQUAL : "equal",
+        IGNORECASE : "ignoreCase",
+        IN : "in",
+        ANY_IN : "anyIn",
+        LIKE : "like",
+        LIKEIGNORECASE : "likeIgnoreCase",
+        
+        _filters : [],
+        addIfSet : function (pUserPropertyName, pFilterValue, pType)
+        {
+            if (pFilterValue && pFilterValue.length !== 0)
+                this.add(pUserPropertyName, pFilterValue, pType);
+        },
+        add : function (pUserPropertyName, pFilterValue, pType)
+        {
+            this._filters.push([pUserPropertyName, pFilterValue, pType]);
+        },
+        check : function  (pUser)
+        {
+            return this._filters.every(function ([prop, value, type])
+            {
+                let userValue = pUser[prop] || "";
+                let flags;
+                switch (type)
+                {
+                    case this.EQUAL:
+                        return userValue == value;
+                    case this.IGNORECASE:
+                        return userValue.toUpperCase() == value.toUpperCase();
+                    case this.IN:
+                        if (Array.isArray(userValue))
+                            return userValue.indexOf(value) !== -1;
+                        return value.indexOf(userValue) !== -1;
+                    case this.ANY_IN:
+                        return value.some(function (val) {return userValue.indexOf(val) !== -1;});
+                    case this.LIKEIGNORECASE:
+                        flags = "i";
+                    case this.LIKE:
+                        value = text.replaceAll(value, {"%" : ".*", "_" : "."});
+                        return new RegExp(value, flags).test(userValue);
+                }
+                return false;
+            }, this);
+        }
+    };
+    
+    filter.addIfSet("id", userFilter.id, filter.EQUAL);
+    filter.addIfSet("id", userFilter.ids && JSON.parse(userFilter.ids), filter.IN);
+    filter.addIfSet("id", userFilter.idIgnoreCase, filter.IGNORECASE);
+    filter.addIfSet("lastName", userFilter.lastName, filter.EQUAL);
+    filter.addIfSet("lastName", userFilter.lastNameLike, filter.LIKE);
+    filter.addIfSet("lastName", userFilter.lastNameLikeIgnoreCase, filter.LIKEIGNORECASE);
+    filter.addIfSet("firstName", userFilter.firstName, filter.EQUAL);
+    filter.addIfSet("firstName", userFilter.firstNameLike, filter.LIKE);
+    filter.addIfSet("firstName", userFilter.firstNameLikeIgnoreCase, filter.LIKEIGNORECASE);
+    filter.addIfSet("fullName", userFilter.displayName, filter.EQUAL);
+    filter.addIfSet("fullName", userFilter.displayNameLike, filter.LIKE);
+    filter.addIfSet("fullName", userFilter.displayNameLikeIgnoreCase, filter.LIKEIGNORECASE);
+    filter.addIfSet("fullName", userFilter.fullNameLike, filter.LIKE);
+//    filter.addIfSet("fullName", userFilter.Fullnamelikeignorecase, filter.LIKEIGNORECASE);
+    filter.addIfSet("email", userFilter.email, filter.EQUAL);
+    filter.addIfSet("email", userFilter.emailLike, filter.LIKE);
+    filter.addIfSet("groups", userFilter.groupIds && JSON.parse(userFilter.groupIds), filter.ANY_IN);
+    filter.addIfSet("groups", userFilter.groupId, filter.IN);
+        
+    request.response.body = JSON.stringify(_getUsers(userFilter.fullNameLikeIgnoreCase));
 
     return JSON.stringify(request);
 
     function _getUsers (pSearch)
     {
-        if (pSearch === undefined || pSearch.trim() < MIN_SEARCHLENGTH)
+        if (pSearch !== undefined && pSearch.trim() < MIN_SEARCHLENGTH)
             return [];
         
         //separate words by spaces
-        let patterns = pSearch.toUpperCase().split(/\s/).filter(function (pat) {return pat;});
+        let patterns = pSearch && pSearch.replace("%", "", "g").toUpperCase().split(/\s/).filter(function (pat) {return pat;})
+        
         
         let allUsers = tools.getUsersByAttribute(tools.ISACTIVE, ["true"], tools.PROFILE_DEFAULT);
         let resultUsers = [];
@@ -30,25 +102,25 @@ function restget (pRequest)
         for (let i = 0, l = allUsers.length; i < l; i++)
         {
             let user = allUsers[i];
-            let id = user[tools.NAME];
             let firstName = user[tools.PARAMS][tools.FIRSTNAME];
             let lastName = user[tools.PARAMS][tools.LASTNAME];
-            let email = user[tools.EMAIL];
-            let rating = _getRating(patterns, [firstName.toUpperCase(), lastName.toUpperCase()]);
-            if (rating !== 0)
-            {
-                resultUsers.push({
-                    id : id,
-                    firstName : firstName,
-                    lastName : lastName,
-                    email : email,
-                    fullName : (firstName + " " + lastName).trim(),
-                    tenantId : null,
-                    groups : [],
-                    privileges : [],
-                    searchRating : rating
-                });
-            }
+            let rating = patterns
+                ? _getRating(patterns, [firstName.toUpperCase(), lastName.toUpperCase()])
+                : 1;
+                
+            user = {
+                id : user[tools.NAME],
+                firstName : firstName,
+                lastName : lastName,
+                email : user[tools.PARAMS][tools.EMAIL],
+                fullName : (firstName + " " + lastName).trim(),
+                tenantId : null,
+                groups : user[tools.ROLENAMES].slice(),
+                privileges : [],
+                searchRating : rating
+            };
+            if (filter.check(user) && rating !== 0)
+                resultUsers.push(user);
         }
         
         //sort by result quality
@@ -56,7 +128,7 @@ function restget (pRequest)
         {
             return b.searchRating - a.searchRating;
         });
-
+        
         return resultUsers.slice(0, MAX_COUNT);
     }
 
diff --git a/process/workflowUsers_rest/workflowUsers_rest.aod b/process/workflowUsers_rest/workflowUsers_rest.aod
index 2b745c8a376..c44615d096e 100644
--- a/process/workflowUsers_rest/workflowUsers_rest.aod
+++ b/process/workflowUsers_rest/workflowUsers_rest.aod
@@ -7,8 +7,7 @@
   <style>REST</style>
   <restAcceptedMimeType>application/json</restAcceptedMimeType>
   <restDeliveredMimeType>application/json</restDeliveredMimeType>
-  <loginTypeId>internal.none</loginTypeId>
-  <restrictedRoles />
+  <jditoWebserviceUser>flowableIdmService</jditoWebserviceUser>
   <variants>
     <element>EXECUTABLE</element>
   </variants>
diff --git a/role/PROJECT_Workflow/PROJECT_Workflow.aod b/role/PROJECT_Workflow/PROJECT_Workflow.aod
new file mode 100644
index 00000000000..06f9449d5f5
--- /dev/null
+++ b/role/PROJECT_Workflow/PROJECT_Workflow.aod
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<role xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.0" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/role/1.2.0">
+  <name>PROJECT_Workflow</name>
+  <title>Workflow management</title>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+</role>
-- 
GitLab