From d238f97214135036aedddcfe5c04eff3f13b50e1 Mon Sep 17 00:00:00 2001
From: Johannes Goderbauer <j.goderbauer@adito.de>
Date: Tue, 5 May 2020 11:26:35 +0000
Subject: [PATCH] [Projekt: Entwicklung - Neon][TicketNr.: 1054152][Sync
 Kalenderberechtigungen mit EWS] added comments

---
 .../2020.1.0/PermissionCalendar/changelog.xml |   1 -
 .../init_PermissionCalendarCategory.xml       |  42 --
 .../Exchange_devIntern/Exchange_devIntern.aod |   6 +
 .../KeywordAttribute_entity.aod               |   1 +
 .../grantDeleteProcess.js                     |   0
 .../KeywordEntry_entity.aod                   |   6 -
 .../Organisation_entity.aod                   |   3 +
 .../PermissionCalendar_entity.aod             |  17 +-
 .../onActionProcess.js                        |   3 +-
 .../onActionProcess.js                        |   3 +-
 .../containername_param/valueProcess.js       |   6 -
 .../permission/dropDownProcess.js             |  12 +
 .../mandatoryProcess.js                       |   3 +-
 .../stateProcess.js                           |   3 +-
 .../mandatoryProcess.js                       |   3 +-
 .../stateProcess.js                           |   3 +-
 .../PermissionCalendar_entity/onValidation.js |   5 +-
 .../_____LANGUAGE_EXTRA.aod                   |  39 ++
 .../_____LANGUAGE_de/_____LANGUAGE_de.aod     |  43 ++
 .../_____LANGUAGE_en/_____LANGUAGE_en.aod     |  36 ++
 process/EwsClient_lib/EwsClient_lib.aod       |   9 +
 process/EwsClient_lib/process.js              | 405 ++++++++++++++++++
 process/KeywordRegistry_basic/process.js      |   5 +-
 .../PermissionCalendar_lib.aod                |   1 +
 process/PermissionCalendar_lib/process.js     | 163 ++++---
 ...sSyncCalendarPermissions_serverProcess.aod |  11 +
 .../process.js                                |   3 +
 27 files changed, 700 insertions(+), 132 deletions(-)
 delete mode 100644 .liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/init_PermissionCalendarCategory.xml
 create mode 100644 aliasDefinition/Exchange_devIntern/Exchange_devIntern.aod
 create mode 100644 entity/KeywordAttribute_entity/grantDeleteProcess.js
 delete mode 100644 entity/PermissionCalendar_entity/entityfields/keywordpermissioncalendarcategories/children/containername_param/valueProcess.js
 create mode 100644 entity/PermissionCalendar_entity/entityfields/permission/dropDownProcess.js
 create mode 100644 process/EwsClient_lib/EwsClient_lib.aod
 create mode 100644 process/EwsClient_lib/process.js
 create mode 100644 process/ewsSyncCalendarPermissions_serverProcess/ewsSyncCalendarPermissions_serverProcess.aod
 create mode 100644 process/ewsSyncCalendarPermissions_serverProcess/process.js

diff --git a/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/changelog.xml b/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/changelog.xml
index a540df4133..25028737ab 100644
--- a/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/changelog.xml
+++ b/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/changelog.xml
@@ -2,6 +2,5 @@
 <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
 <include file="create_ab_permissioncalendar.xml" relativeToChangelogFile="true"/>
-<include file="init_PermissionCalendarCategory.xml" relativeToChangelogFile="true"/>
 <include file="init_PermissionCalendarType.xml" relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/init_PermissionCalendarCategory.xml b/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/init_PermissionCalendarCategory.xml
deleted file mode 100644
index dafd18910f..0000000000
--- a/.liquibase/Data_alias/basic/2020.1.0/PermissionCalendar/init_PermissionCalendarCategory.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" standalone="no"?>
-<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
-                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
-    <changeSet author="s.pongratz" id="b2394de0-4a19-4fcb-9412-0a5e91bfec82">
-        <insert tableName="AB_KEYWORD_ENTRY">
-            <column name="AB_KEYWORD_ENTRYID" value="c5f3cae8-06b3-4db3-8c7e-80f16e6d080e"/>
-            <column name="KEYID" value="NOTHING"/>
-            <column name="TITLE" value="NoPermissions"/>
-            <column name="CONTAINER" value="PermissionCalendarCategory"/>
-            <column name="SORTING" valueNumeric="0"/>
-            <column name="ISACTIVE" valueNumeric="1"/>
-            <column name="ISESSENTIAL" valueNumeric="1"/>
-        </insert>
-        <insert tableName="AB_KEYWORD_ENTRY">
-            <column name="AB_KEYWORD_ENTRYID" value="c2624c60-cb07-44f2-80b1-fbb86932df72"/>
-            <column name="KEYID" value="LIMITEDREAD"/>
-            <column name="TITLE" value="LimitedReadPermission"/>
-            <column name="CONTAINER" value="PermissionCalendarCategory"/>
-            <column name="SORTING" valueNumeric="1"/>
-            <column name="ISACTIVE" valueNumeric="1"/>
-            <column name="ISESSENTIAL" valueNumeric="1"/>
-        </insert>
-        <insert tableName="AB_KEYWORD_ENTRY">
-            <column name="AB_KEYWORD_ENTRYID" value="64546657-843b-4cb4-a6b8-4d34548e1c97"/>
-            <column name="KEYID" value="FULLREAD"/>
-            <column name="TITLE" value="ReadPermission"/>
-            <column name="CONTAINER" value="PermissionCalendarCategory"/>
-            <column name="SORTING" valueNumeric="2"/>
-            <column name="ISACTIVE" valueNumeric="1"/>
-            <column name="ISESSENTIAL" valueNumeric="1"/>
-        </insert>
-        <insert tableName="AB_KEYWORD_ENTRY">
-            <column name="AB_KEYWORD_ENTRYID" value="64546657-843b-4cb4-a6b8-4d34548e1c98"/>
-            <column name="KEYID" value="READWRITE"/>
-            <column name="TITLE" value="ReadWritePermission"/>
-            <column name="CONTAINER" value="PermissionCalendarCategory"/>
-            <column name="SORTING" valueNumeric="3"/>
-            <column name="ISACTIVE" valueNumeric="1"/>
-            <column name="ISESSENTIAL" valueNumeric="1"/>
-        </insert>
-    </changeSet>
-</databaseChangeLog>
diff --git a/aliasDefinition/Exchange_devIntern/Exchange_devIntern.aod b/aliasDefinition/Exchange_devIntern/Exchange_devIntern.aod
new file mode 100644
index 0000000000..91b436cae6
--- /dev/null
+++ b/aliasDefinition/Exchange_devIntern/Exchange_devIntern.aod
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<aliasDefinition 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/aliasDefinition/1.2.0">
+  <name>Exchange_devIntern</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <datasourceType v="10" />
+</aliasDefinition>
diff --git a/entity/KeywordAttribute_entity/KeywordAttribute_entity.aod b/entity/KeywordAttribute_entity/KeywordAttribute_entity.aod
index 3c1336f58b..ac1185cf4b 100644
--- a/entity/KeywordAttribute_entity/KeywordAttribute_entity.aod
+++ b/entity/KeywordAttribute_entity/KeywordAttribute_entity.aod
@@ -4,6 +4,7 @@
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/KeywordAttribute_entity/documentation.adoc</documentation>
   <title>Keyword Attribute</title>
+  <grantDeleteProcess>%aditoprj%/entity/KeywordAttribute_entity/grantDeleteProcess.js</grantDeleteProcess>
   <contentTitleProcess>%aditoprj%/entity/KeywordAttribute_entity/contentTitleProcess.js</contentTitleProcess>
   <iconId>VAADIN:KEY_O</iconId>
   <iconIdProcess>%aditoprj%/entity/KeywordAttribute_entity/iconIdProcess.js</iconIdProcess>
diff --git a/entity/KeywordAttribute_entity/grantDeleteProcess.js b/entity/KeywordAttribute_entity/grantDeleteProcess.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/entity/KeywordEntry_entity/KeywordEntry_entity.aod b/entity/KeywordEntry_entity/KeywordEntry_entity.aod
index 36b66e6474..1d23aa80c1 100644
--- a/entity/KeywordEntry_entity/KeywordEntry_entity.aod
+++ b/entity/KeywordEntry_entity/KeywordEntry_entity.aod
@@ -553,12 +553,6 @@
           <fieldName>ContextDocumentTemplatePlaceOfUse</fieldName>
           <isConsumer v="false" />
         </entityDependency>
-        <entityDependency>
-          <name>79bdbc02-19dc-4ecc-a91f-d657bda6f6cd</name>
-          <entityName>PermissionCalendar_entity</entityName>
-          <fieldName>KeywordPermissionCalendarCategories</fieldName>
-          <isConsumer v="false" />
-        </entityDependency>
         <entityDependency>
           <name>8c5b1d49-f0b5-4a7c-9bff-02165996e11b</name>
           <entityName>PermissionCalendar_entity</entityName>
diff --git a/entity/Organisation_entity/Organisation_entity.aod b/entity/Organisation_entity/Organisation_entity.aod
index 46d2089ce3..8ccbb9a42d 100644
--- a/entity/Organisation_entity/Organisation_entity.aod
+++ b/entity/Organisation_entity/Organisation_entity.aod
@@ -1289,6 +1289,9 @@
           <filterConditionProcess>%aditoprj%/entity/Organisation_entity/recordcontainers/db/filterextensions/attribute_filter/filterConditionProcess.js</filterConditionProcess>
           <filtertype>BASIC</filtertype>
         </filterExtensionSet>
+        <filterExtension>
+          <name>filterExtension</name>
+        </filterExtension>
       </filterExtensions>
     </dbRecordContainer>
     <indexRecordContainer>
diff --git a/entity/PermissionCalendar_entity/PermissionCalendar_entity.aod b/entity/PermissionCalendar_entity/PermissionCalendar_entity.aod
index 552e3a6bfe..1561a02279 100644
--- a/entity/PermissionCalendar_entity/PermissionCalendar_entity.aod
+++ b/entity/PermissionCalendar_entity/PermissionCalendar_entity.aod
@@ -16,9 +16,9 @@
     <entityField>
       <name>PERMISSION</name>
       <title>permission</title>
-      <consumer>KeywordPermissionCalendarCategories</consumer>
       <groupable v="true" />
       <mandatory v="true" />
+      <dropDownProcess>%aditoprj%/entity/PermissionCalendar_entity/entityfields/permission/dropDownProcess.js</dropDownProcess>
       <displayValueProcess>%aditoprj%/entity/PermissionCalendar_entity/entityfields/permission/displayValueProcess.js</displayValueProcess>
     </entityField>
     <entityField>
@@ -26,20 +26,6 @@
       <title>ID</title>
       <mandatory v="false" />
     </entityField>
-    <entityConsumer>
-      <name>KeywordPermissionCalendarCategories</name>
-      <dependency>
-        <name>dependency</name>
-        <entityName>KeywordEntry_entity</entityName>
-        <fieldName>SpecificContainerKeywords</fieldName>
-      </dependency>
-      <children>
-        <entityParameter>
-          <name>ContainerName_param</name>
-          <valueProcess>%aditoprj%/entity/PermissionCalendar_entity/entityfields/keywordpermissioncalendarcategories/children/containername_param/valueProcess.js</valueProcess>
-        </entityParameter>
-      </children>
-    </entityConsumer>
     <entityField>
       <name>USER_NEW</name>
       <mandatory v="false" />
@@ -142,6 +128,7 @@
     </entityProvider>
     <entityParameter>
       <name>PermissionProcurer_param</name>
+      <valueProcess>%aditoprj%/entity/PermissionCalendar_entity/entityfields/permissionprocurer_param/valueProcess.js</valueProcess>
       <expose v="true" />
     </entityParameter>
     <entityParameter>
diff --git a/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewdepartmentpermissiondealteraction/onActionProcess.js b/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewdepartmentpermissiondealteraction/onActionProcess.js
index 16ae742310..78b952ce03 100644
--- a/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewdepartmentpermissiondealteraction/onActionProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewdepartmentpermissiondealteraction/onActionProcess.js
@@ -1,8 +1,9 @@
+import("KeywordRegistry_basic");
 import("PermissionCalendar_lib");
 import("system.vars");
 import("system.neon");
 
 var params = {};
-params["PermissionDealerType_param"] = PermissionCalendar.objectTypeDepartment();
+params["PermissionDealerType_param"] = $KeywordRegistry.permissionCalendarType$department();
 params["PermissionProcurer_param"] = vars.get("$param.PermissionProcurer_param");
 neon.openContext("PermissionCalendar", null, null, neon.OPERATINGSTATE_NEW, params);
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewuserpermissiondealeraction/onActionProcess.js b/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewuserpermissiondealeraction/onActionProcess.js
index d2946f2d5c..0aee6ca71f 100644
--- a/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewuserpermissiondealeraction/onActionProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/addactions/children/addnewuserpermissiondealeraction/onActionProcess.js
@@ -1,8 +1,9 @@
+import("KeywordRegistry_basic");
 import("PermissionCalendar_lib");
 import("system.vars");
 import("system.neon");
 
 var params = {};
-params["PermissionDealerType_param"] = PermissionCalendar.objectTypeUser();
+params["PermissionDealerType_param"] = $KeywordRegistry.permissionCalendarType$user();
 params["PermissionProcurer_param"] = vars.get("$param.PermissionProcurer_param");
 neon.openContext("PermissionCalendar", null, null, neon.OPERATINGSTATE_NEW, params);
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/keywordpermissioncalendarcategories/children/containername_param/valueProcess.js b/entity/PermissionCalendar_entity/entityfields/keywordpermissioncalendarcategories/children/containername_param/valueProcess.js
deleted file mode 100644
index 7f169e4a01..0000000000
--- a/entity/PermissionCalendar_entity/entityfields/keywordpermissioncalendarcategories/children/containername_param/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.result");
-import("Keyword_lib");
-import("KeywordRegistry_basic");
-import("PermissionCalendar_lib");
-
-result.string($KeywordRegistry.permissionCalendarCategory());
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/permission/dropDownProcess.js b/entity/PermissionCalendar_entity/entityfields/permission/dropDownProcess.js
new file mode 100644
index 0000000000..1acac0c3cb
--- /dev/null
+++ b/entity/PermissionCalendar_entity/entityfields/permission/dropDownProcess.js
@@ -0,0 +1,12 @@
+import("system.result");
+import("PermissionCalendar_lib");
+
+var setableCategoryIds = PermissionCalendar.getSetableCategories();
+var allCategories = PermissionCalendar.getCategories();
+
+var res = setableCategoryIds.map(function (categoryId){
+    var title = allCategories[categoryId] ? allCategories[categoryId].getTitle() : categoryId;
+   return [categoryId, title];
+});
+
+result.object(res);
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/mandatoryProcess.js b/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/mandatoryProcess.js
index a31a0c05cc..b0dd49473c 100644
--- a/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/mandatoryProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/mandatoryProcess.js
@@ -1,8 +1,9 @@
+import("KeywordRegistry_basic");
 import("system.vars");
 import("system.result");
 import("PermissionCalendar_lib");
 
-if(vars.getString("$field.PERMISSIONDEALER_TYPE") == PermissionCalendar.objectTypeDepartment())
+if(vars.getString("$field.PERMISSIONDEALER_TYPE") == $KeywordRegistry.permissionCalendarType$department())
     result.string(true);
 else
     result.string(false);
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/stateProcess.js b/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/stateProcess.js
index 88016954f0..9d47dab7c4 100644
--- a/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/stateProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/permissiondealer_department_rowid/stateProcess.js
@@ -1,9 +1,10 @@
+import("KeywordRegistry_basic");
 import("system.vars");
 import("system.result");
 import("PermissionCalendar_lib");
 import("system.neon");
 
-if(vars.getString("$field.PERMISSIONDEALER_TYPE") == PermissionCalendar.objectTypeDepartment())
+if(vars.getString("$field.PERMISSIONDEALER_TYPE") == $KeywordRegistry.permissionCalendarType$department())
     if(vars.getString("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)
         result.string("READONLY")
     else
diff --git a/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/mandatoryProcess.js b/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/mandatoryProcess.js
index aedb9baf38..20c989da57 100644
--- a/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/mandatoryProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/mandatoryProcess.js
@@ -1,8 +1,9 @@
+import("KeywordRegistry_basic");
 import("system.vars");
 import("system.result");
 import("PermissionCalendar_lib");
 
-if(vars.getString("$field.PERMISSIONDEALER_TYPE") == PermissionCalendar.objectTypeUser())
+if(vars.getString("$field.PERMISSIONDEALER_TYPE") == $KeywordRegistry.permissionCalendarType$user())
     result.string(true);
 else
     result.string(false);
\ No newline at end of file
diff --git a/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/stateProcess.js b/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/stateProcess.js
index 3b15f8f615..6176f62e41 100644
--- a/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/stateProcess.js
+++ b/entity/PermissionCalendar_entity/entityfields/permissiondealer_user_rowid/stateProcess.js
@@ -1,9 +1,10 @@
+import("KeywordRegistry_basic");
 import("system.vars");
 import("system.result");
 import("PermissionCalendar_lib");
 import("system.neon");
 
-if(vars.getString("$field.PERMISSIONDEALER_TYPE") == PermissionCalendar.objectTypeUser())
+if(vars.getString("$field.PERMISSIONDEALER_TYPE") == $KeywordRegistry.permissionCalendarType$user())
     if(vars.getString("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)
         result.string("READONLY")
     else
diff --git a/entity/PermissionCalendar_entity/onValidation.js b/entity/PermissionCalendar_entity/onValidation.js
index 7830911e9f..61a3f0facf 100644
--- a/entity/PermissionCalendar_entity/onValidation.js
+++ b/entity/PermissionCalendar_entity/onValidation.js
@@ -1,3 +1,4 @@
+import("KeywordRegistry_basic");
 import("system.neon");
 import("system.translate");
 import("system.result");
@@ -7,9 +8,9 @@ import("Employee_lib");
 
 let dealerId;
 let dealerType = vars.getString("$field.PERMISSIONDEALER_TYPE");
-if(dealerType == PermissionCalendar.objectTypeUser())
+if(dealerType == $KeywordRegistry.permissionCalendarType$user())
     dealerId = vars.getString("$field.PERMISSIONDEALER_USER_ROWID");
-else if (dealerType == PermissionCalendar.objectTypeDepartment())
+else if (dealerType == $KeywordRegistry.permissionCalendarType$department())
     dealerId = vars.getString("$field.PERMISSIONDEALER_DEPARTMENT_ROWID");
 else
     dealerId = "";
diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
index 1041f23b11..fc6870e2c1 100644
--- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
+++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
@@ -6701,6 +6701,45 @@
     <entry>
       <key>New Visit Recommendation</key>
     </entry>
+    <entry>
+      <key>Invalid file type; only .html, .eml and .txt are allowed</key>
+    </entry>
+    <entry>
+      <key>ankle of</key>
+    </entry>
+    <entry>
+      <key>New Visit Recommendation</key>
+    </entry>
+    <entry>
+      <key>Limited details</key>
+    </entry>
+    <entry>
+      <key>Free or Busy Permission</key>
+    </entry>
+    <entry>
+      <key>Full Read Permission</key>
+    </entry>
+    <entry>
+      <key>No permissions</key>
+    </entry>
+    <entry>
+      <key>Editor</key>
+    </entry>
+    <entry>
+      <key>Read and Write Permission</key>
+    </entry>
+    <entry>
+      <key>Synchronize calendar permissions from Exchange to ADITO</key>
+    </entry>
+    <entry>
+      <key>Availability only</key>
+    </entry>
+    <entry>
+      <key>Limited Read Permission</key>
+    </entry>
+    <entry>
+      <key>Full details</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 e1906b85ea..840ac9df1d 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -8589,6 +8589,49 @@ Bitte Datumseingabe prüfen</value>
     <entry>
       <key>Linked In</key>
     </entry>
+    <entry>
+      <key>Abomodel</key>
+    </entry>
+    <entry>
+      <key>Limited details</key>
+      <value>Eingeschränkte Details</value>
+    </entry>
+    <entry>
+      <key>Free or Busy Permission</key>
+      <value>Nur Verfügbarkeit</value>
+    </entry>
+    <entry>
+      <key>Full Read Permission</key>
+      <value>Volle Leseberechtigung</value>
+    </entry>
+    <entry>
+      <key>No permissions</key>
+      <value>Keine Zugriffsberechtigung</value>
+    </entry>
+    <entry>
+      <key>Editor</key>
+      <value>Bearbeiter</value>
+    </entry>
+    <entry>
+      <key>Read and Write Permission</key>
+      <value>Volle Lese- und Schreibberechtigung</value>
+    </entry>
+    <entry>
+      <key>Synchronize calendar permissions from Exchange to ADITO</key>
+      <value>Synchronisiere Kalenderberechtigungen von Exchange nach ADITO</value>
+    </entry>
+    <entry>
+      <key>Availability only</key>
+      <value>Nur Verfügbarkeit</value>
+    </entry>
+    <entry>
+      <key>Limited Read Permission</key>
+      <value>Eingeschränkte Leseberechtigung</value>
+    </entry>
+    <entry>
+      <key>Full details</key>
+      <value>Alle Details</value>
+    </entry>
   </keyValueMap>
   <font name="Dialog" style="0" size="11" />
 </language>
diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
index 60030d8ecb..73a7ba3dbe 100644
--- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
+++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
@@ -6766,6 +6766,42 @@
     <entry>
       <key>ankle of</key>
     </entry>
+    <entry>
+      <key>Visit Planning</key>
+    </entry>
+    <entry>
+      <key>ankle of</key>
+    </entry>
+    <entry>
+      <key>Limited details</key>
+    </entry>
+    <entry>
+      <key>Free or Busy Permission</key>
+    </entry>
+    <entry>
+      <key>Full Read Permission</key>
+    </entry>
+    <entry>
+      <key>No permissions</key>
+    </entry>
+    <entry>
+      <key>Editor</key>
+    </entry>
+    <entry>
+      <key>Read and Write Permission</key>
+    </entry>
+    <entry>
+      <key>Synchronize calendar permissions from Exchange to ADITO</key>
+    </entry>
+    <entry>
+      <key>Availability only</key>
+    </entry>
+    <entry>
+      <key>Limited Read Permission</key>
+    </entry>
+    <entry>
+      <key>Full details</key>
+    </entry>
   </keyValueMap>
   <font name="Dialog" style="0" size="11" />
 </language>
diff --git a/process/EwsClient_lib/EwsClient_lib.aod b/process/EwsClient_lib/EwsClient_lib.aod
new file mode 100644
index 0000000000..6343b20344
--- /dev/null
+++ b/process/EwsClient_lib/EwsClient_lib.aod
@@ -0,0 +1,9 @@
+<?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>EwsClient_lib</name>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <process>%aditoprj%/process/EwsClient_lib/process.js</process>
+  <variants>
+    <element>LIBRARY</element>
+  </variants>
+</process>
diff --git a/process/EwsClient_lib/process.js b/process/EwsClient_lib/process.js
new file mode 100644
index 0000000000..41e53a9c9a
--- /dev/null
+++ b/process/EwsClient_lib/process.js
@@ -0,0 +1,405 @@
+import("KeywordRegistry_basic");
+import("system.datetime");
+import("Employee_lib");
+import("system.util");
+import("system.vars");
+import("PermissionCalendar_lib");
+import("system.db");
+import("system.logging");
+import("system.tools");
+import("system.project");
+import("system.plugin");
+import("Sql_lib");
+
+
+
+/**
+ * Provides static methods various actions for the EWS plugin
+ * 
+ * @class
+ */
+function EwsClientUtils(){}
+
+
+/**
+ * performs a plugin call of the EWS client plugin
+ *
+ * @param {XML} pPluginInput          <p/> 
+ *                                      Plugin Request that determines what the plugin shall do and to which exchange server the plugin shall connect
+ * @return {XML}                      <p/> Depending on the pPluginInput, the result of the plugin. Note that not always a error is thrown when there
+ *                                         are no permissions for a folder. In some of these cases a XML is returned that does not contain any data 
+ * @static 
+ */
+EwsClientUtils.callPlugin = function (pPluginInput)
+{
+    if (pPluginInput && typeof(pPluginInput) != "string")//for better compability with legacy code expect a string or a XML object
+        pPluginInput = pPluginInput.toString();
+    var pluginPath = null;
+    var res = plugin.run(pluginPath, "de.adito.plugin.ewsclient.EwsClientPlugin", [pPluginInput])[0];
+    return new XML(res);
+};
+
+/*this is right now (2020-04-15) the only way to get something like a constant with 
+ - a prober name
+ - autocomplete support within the ADITO Designer
+ - and proper packageing all constants together
+This may change in the future, if there is something better feel free to improve this */
+function $EwsClientTaskDescriptions(){}
+$EwsClientTaskDescriptions.GET_EMAIL_IDS =                      function(){return "GET_EMAIL_IDS"};
+$EwsClientTaskDescriptions.GET_EMAIL_BY_ID =                    function(){return "GET_EMAIL_BY_ID"};
+$EwsClientTaskDescriptions.GET_EMAILS =                         function(){return "GET_EMAILS"};
+$EwsClientTaskDescriptions.DELETE_EMAIL_BY_ID =                 function(){return "DELETE_EMAIL_BY_ID"};
+$EwsClientTaskDescriptions.INSERT_EMAIL =                       function(){return "INSERT_EMAIL"};
+$EwsClientTaskDescriptions.GET_CALENDAR_TASKS_PERMISSIONS =     function(){return "GET_CALENDAR_TASKS_PERMISSIONS"};
+$EwsClientTaskDescriptions.DELETE_CONTACT =                     function(){return "DELETE_CONTACT"};
+$EwsClientTaskDescriptions.DELETE_CONTACT_BY_ID =               function(){return "DELETE_CONTACT_BY_ID"};
+$EwsClientTaskDescriptions.INSERT_CONTACT =                     function(){return "INSERT_CONTACT"};
+$EwsClientTaskDescriptions.INSERT_CONTACT_TO_FOLDER =           function(){return "INSERT_CONTACT_TO_FOLDER"};
+$EwsClientTaskDescriptions.GET_CONTACT_BY_ID =                  function(){return "GET_CONTACT_BY_ID"};
+$EwsClientTaskDescriptions.GET_CONTACTS =                       function(){return "GET_CONTACTS"};
+$EwsClientTaskDescriptions.GET_CONTACTS_BY_CATEGORIE =          function(){return "GET_CONTACTS_BY_CATEGORIE"};
+$EwsClientTaskDescriptions.GET_CONTACTS_BY_FOLDERS =            function(){return "GET_CONTACTS_BY_FOLDERS"};
+$EwsClientTaskDescriptions.GET_CONTACTSDEFAULTFOLDER =          function(){return "GET_CONTACTSDEFAULTFOLDER"};
+$EwsClientTaskDescriptions.GET_CONTACTFOLDERS =                 function(){return "GET_CONTACTFOLDERS"};
+$EwsClientTaskDescriptions.UPDATE_CONTACT =                     function(){return "UPDATE_CONTACT"};
+
+function $EwsClientImpersonationModes(){}
+/**
+ * Impersonation mode NONE should be set if no impersonation user is used and every user is authenticated per user-data
+ */
+$EwsClientImpersonationModes.NONE =     function(){return "NONE"};
+/**
+ * Impersonation mode SINGLE should be set if an impersonation user is used that impersonates ONE user (e.g. for syncing a contact folder)
+ */
+$EwsClientImpersonationModes.SINGLE =   function(){return "SINGLE"};
+/**
+ * Impersonation mode LIST should be set if an impersonation user is used that impersonates SEVERAL users (e.g. for loading the calendar permissions)
+ */
+$EwsClientImpersonationModes.LIST =     function(){return "LIST"};
+
+/**
+ * Provides static methods for handling and creating XMLs to interact with the EWS plugin
+ * 
+ * @class
+ */
+function EwsClientXMLUtils(){}
+
+/**
+ * builds a request XML that can be given to the EWS client plugin
+ * uses basic auth with user and password for authentication
+ *
+ * @param {String} pUrl                     <p/> The complete URL that points to the Exchange server, this has to point at the Exchange.asmx 
+ *                                          <br/>e.g.: "https://exchange.mycustomer.com:443/ews/Exchange.asmx"
+ * @param {String} pDomain                  <p/> Domain that is used for the connection, this may differ to the domain in pUrl (e.g. Exchange online)
+ * @param {String} pImpersonatedUserMode    <p/> Specifies if the pUser is a impersonation user or not. This has to be a value of 
+ *                                               <pre>$EwsClientImpersonationModes.***</pre>
+ *                                          <br/>See the description of the values there for more information
+ * @param {String} pTaskDescription         <p/> type of the task that the ews client plugin is performing, this has to be a value of 
+ *                                               <pre>$EwsClientTaskDescriptions.***</pre>
+ * @param {XML} pTaskXML                    <p/> Depending on the pTaskDescription different information have to be provided to the plugin.
+ *                                          <br/>In the pTaskXML param all data that is needed for a given task type has to be provided as XML object
+  * @param {String} pUser                   <p/> Exchange user that is used for authenticating
+ *                                          <br/>Keep in mind that you have to set the pImpersonatedUserMode depending on if the pUser is a 
+ *                                               impersonation user or not.
+ * @param {String} pPassword                <p/> Matching password to authenticate the pUser 
+ *
+ * @return {XML}                            <p/> Ready to use XML request that can be provided to the EWS client plugin.
+ *                                          <br/><i>Remember that if you want to log the XML-object you need to call .toString() onto the result</i>
+ * 
+ * @static 
+ */
+EwsClientXMLUtils.getRequestXMLbasicAuth = function (pUrl, pDomain, pImpersonatedUserMode, pTaskDescription, pTaskXML, pUser, pPassword)
+{
+    //use xml syntax with placeholders so that the placeholder values are automatically escaped (this is necessary for complex passwords)
+    var request = <ewsClientPlugin>
+        <connection>
+            <connectionURL>{pUrl}</connectionURL>
+            <connectionUser>{pUser}</connectionUser>
+            <connectionPassword>{pPassword}</connectionPassword>
+            <connectionDomain>{pDomain}</connectionDomain>
+            <impersonatedUserMode>{pImpersonatedUserMode}</impersonatedUserMode>
+        </connection>
+        <task>
+            <description>{pTaskDescription}</description>
+        </task>
+    </ewsClientPlugin>;
+
+    //since pTaskXML is of type XML we cannot just use the template mechanism to add the content since the XML would be escaped
+    //so instead inject it direct as part of XML with  the appendChild method of XML-objects
+    request.task.appendChild(pTaskXML);
+
+    return request;
+};
+
+/**
+ * builds a request XML that can be given to the EWS client plugin
+ * uses basic auth with user and password for authentication, the data is automatically loaded from a given exchange (web service) alias
+ *
+ * @param {String} pTaskDescription         <p/> type of the task that the ews client plugin is performing, this has to be a value of 
+ *                                               <pre>$EwsClientTaskDescriptions.***</pre>
+ * @param {XML} pTaskXML                    <p/> Depending on the pTaskDescription different information have to be provided to the plugin.
+ *                                          <br/>In the pTaskXML param all data that is needed for a given task type has to be provided as XML object
+ * @param {String} pImpersonatedUserMode    <p/> Specifies if impersonation (when yes: which one) is used for authentication or not. 
+ *                                               This has to be a value of  <pre>$EwsClientImpersonationModes.***</pre>
+ *                                          <br/>See the description of the values there for more information
+ * @param {String} pAliasName               <p/> name of the alias where to load the data for authentication and the host, port, etc.
+ *                                          
+ * @return {XML}                            <p/> Ready to use XML request that can be provided to the EWS client plugin.
+ *                                          <br/><i>Remember that if you want to log the XML-object you need to call .toString() onto the result</i>
+ * 
+ * @static 
+ */
+EwsClientXMLUtils.getRequestXMLforAlias = function (pTaskDescription, pTaskXML, pImpersonatedUserMode, pAliasName)
+{
+    var alias = project.getAliasModel(pAliasName);
+    var url;
+    if( alias[project.ALIAS_PROPERTIES]["security"] == 2 )
+    {
+        url = "https://" + alias[project.ALIAS_PROPERTIES]["host"] + ":" + alias[project.ALIAS_PROPERTIES]["port"] + "/ews/Exchange.asmx";
+    }
+    else
+    {
+        url = "http://" + alias[project.ALIAS_PROPERTIES]["host"] + ":" + alias[project.ALIAS_PROPERTIES]["port"] + "/ews/Exchange.asmx";
+    }
+    var connectionUser = alias[project.ALIAS_PROPERTIES]["user"];
+    var connectionPassword = alias[project.ALIAS_PROPERTIES]["password"];
+    var connectionDomain = alias[project.ALIAS_PROPERTIES]["domain"];
+
+    return EwsClientXMLUtils.getRequestXMLbasicAuth(url, connectionDomain, pImpersonatedUserMode, pTaskDescription, pTaskXML, 
+        connectionUser, connectionPassword);
+};
+
+/**
+ * Provides static methods for calendar permission interactions with the EWS client plugin
+ * 
+ * @class
+ */
+function EwsClientCalendarPermissionUtils(){}
+
+/**
+ * builds a request XML that can be given to the EWS client plugin
+ * uses basic auth with user and password for authentication, the data is automatically loaded from a given exchange (web service) alias
+ *
+ * @param {String} pAliasName               <p/> name of the alias where to load the data for authentication and the host, port, etc.
+ * @param {String} pRequestedMailAddresses  <p/> type of the task that the ews client plugin is performing, this has to be a value of 
+ *                                               <pre>$EwsClientTaskDescriptions.***</pre>
+ *                                               
+ * @return {XML}                            <p/> Ready to use XML request that can be provided to the EWS client plugin.
+ *                                          <br/><i>Remember that if you want to log the XML-object you need to call .toString() onto the result</i>
+ * 
+ * @static 
+ */
+EwsClientCalendarPermissionUtils.getRequestXMLforAlias = function (pAliasName, pRequestedMailAddresses)
+{
+    var task = <requesteds>
+                <requested>CALENDAR</requested>
+               </requesteds>;
+
+    for (var i = 0, l = pRequestedMailAddresses.length; i < l; i++)
+    {
+        var emailAddr = pRequestedMailAddresses[i];
+        task.mails += <mail>{emailAddr}</mail>;
+    }
+
+    var taskDescription = $EwsClientTaskDescriptions.GET_CALENDAR_TASKS_PERMISSIONS();
+    var impersonationMode = $EwsClientImpersonationModes.LIST();
+    var request = EwsClientXMLUtils.getRequestXMLforAlias(taskDescription, task, impersonationMode, pAliasName);
+    return request;
+};
+
+
+/**
+ * builds a request XML that can be given to the EWS client plugin
+ * uses basic auth with user and password for authentication, the data is automatically loaded from a given exchange (web service) alias
+ * 
+ * This function will create records in AB_PERMISSIONCALENDAR or update them if they are existant, but it will *NOT* remove entries thar are not any
+ * more returned from the plugin.
+ *
+ * @param {String} pExchangeAliasName       <p/> name of the EWS-alias where the permissions are loaded
+ *                                               
+ * @return {null}                           <p/> returns currently nothing
+ * 
+ * @static 
+ */
+EwsClientCalendarPermissionUtils.writePermissions = function(pExchangeAliasName)
+{
+    //TODO: refactor this function since this is basically an adjusted version of the legacy-ews-clientsync-code
+    //e.g. sqls in loops or the readability of the code, etc. could be improved here
+
+    //load all users and then filter for exchange-users
+    var storedUsers = tools.getStoredUsers();
+    var exchangeUserMap = {};
+    var requestedUsers = [];//users that a requested within exchange via a plugin call
+    for( var i = 0; i < storedUsers.length; i++)
+    {
+        var storedUser = storedUsers[i];
+        var title = storedUser[1];
+        var aditoUser = tools.getUser(title);
+        var userId = EmployeeUtils.sliceUserId(aditoUser[tools.NAME]);
+        var userParams = aditoUser[tools.PARAMS];
+        var exchangeMail = userParams[tools.EXCHANGE_EMAIL];//maybe alternative is: >>userParams[tools.CALENDARID]<<?
+        if( exchangeMail == undefined || exchangeMail.length == 0 || exchangeMail.search("@exchange.intern") == -1)
+            continue;
+        exchangeUserMap[exchangeMail] = userId;
+        requestedUsers.push(exchangeMail);
+    }
+   
+    var pluginRequest = EwsClientCalendarPermissionUtils.getRequestXMLforAlias(pExchangeAliasName, requestedUsers);
+    //logging.log("INPUT:\n" + pluginRequest.toString());
+    var xmlOutput = EwsClientUtils.callPlugin(pluginRequest);
+    //logging.log("OUTPUT:\n" + xmlOutput.toString());
+    
+    // read explicit and default permissions and buffer them in maps
+    var owners = xmlOutput.folderUsers.folderUser;
+    var owner;
+    var explicitMap = {};
+    var defaultMap = {};
+    for each( owner in owners )//XML objects need a "for each (x in y)" loop and not a "for (x in y)" loop
+    {
+        var ownerMail = owner.mail;
+        var folders = owner.folders.folder;
+        var folderMapDef = {};
+        var folderMapEx = {};
+        for each( var folder in folders )//XML objects need a "for each (x in y)" loop and not a "for (x in y)" loop
+        {
+            var folderName = folder.name;//in therory it is possible read other folder permissions too (e.g. tasks) so let's keep the code generic
+            var qualifiedUsers = folder.qualifiedUsers.qualifiedUser
+            var qualifiedUserMapEx = {};
+            for each( var qualifiedUser in qualifiedUsers )//XML objects need a "for each (x in y)" loop and not a "for (x in y)" loop
+            {
+                var qualifiedUserMail = qualifiedUser.mail 
+                var permissions = qualifiedUser.permissions;
+                if( "Anonymous" == qualifiedUserMail )
+                {
+                    continue
+                }
+                else if( "Default" == qualifiedUserMail )
+                {
+                    folderMapDef[folderName] = permissions;
+                    defaultMap[ownerMail] = folderMapDef;
+                }
+                else
+                {
+                    qualifiedUserMapEx[qualifiedUserMail] = permissions;
+                    folderMapEx[folderName] = qualifiedUserMapEx;
+                    explicitMap[ownerMail] = folderMapEx;
+                }
+            }
+        }
+    }
+    
+    var dbChangeUser = "server";//vars.get("$sys.user") does not work on the serverside (executing from the manager) //todo: check if this is a bug
+    var updateCalCols = new Array("USER_EDIT", "DATE_EDIT", "PERMISSION");
+    var updateCalTypes = db.getColumnTypes("AB_PERMISSIONCALENDAR", updateCalCols);
+    var insertCalCols = new Array("AB_PERMISSIONCALENDARID", "DATE_NEW","USER_NEW",
+        "PERMISSIONPROCURER_ROWID", "PERMISSIONPROCURER_TYPE", "PERMISSIONDEALER_ROWID", "PERMISSIONDEALER_TYPE", "PERMISSION");
+    var insertCalTypes = db.getColumnTypes("AB_PERMISSIONCALENDAR", insertCalCols);
+    var vals;
+    var right;
+    
+    for (var j = 0, l = requestedUsers.length; j < l; j++)
+    {
+        owner = requestedUsers[j];
+        if (defaultMap[owner] == undefined) //maybe more was requested than actually exists on the exchange server; skip these
+            continue;
+        var defaultCalendarPermission = EwsClientCalendarPermissionUtils._getCalendarPermissionFromResult(defaultMap[owner]["CALENDAR"]);
+        for (var jj = 0, ll = requestedUsers.length; jj < ll; jj++)
+        {
+            var qualified = requestedUsers[jj];
+            if( qualified == owner )
+            {
+                continue;
+            }
+            right = defaultCalendarPermission;
+            if( explicitMap[owner] !== undefined && explicitMap[owner]["CALENDAR"] !== undefined && explicitMap[owner]["CALENDAR"][qualified] !== undefined )
+            {
+                right = EwsClientCalendarPermissionUtils._getCalendarPermissionFromResult(explicitMap[owner]["CALENDAR"][qualified]);
+            }
+            
+            // combination of owner und User existant? -> update, otherwise insert
+
+            var existingPermissionQuery = newSelect("AB_PERMISSIONCALENDAR.AB_PERMISSIONCALENDARID, AB_PERMISSIONCALENDAR.PERMISSION")
+                    .from("AB_PERMISSIONCALENDAR")
+                    .where("AB_PERMISSIONCALENDAR.PERMISSIONPROCURER_ROWID", exchangeUserMap[qualified]) //this user...
+                    .and("AB_PERMISSIONCALENDAR.PERMISSIONPROCURER_TYPE", $KeywordRegistry.permissionCalendarType$user()) 
+                    .and("AB_PERMISSIONCALENDAR.PERMISSIONDEALER_ROWID", exchangeUserMap[owner]) //...has right for this user
+                    .and("AB_PERMISSIONCALENDAR.PERMISSIONDEALER_TYPE", $KeywordRegistry.permissionCalendarType$user());
+            var existingPermission = existingPermissionQuery.arrayRow();
+
+            if (existingPermission.length > 0) 
+            {
+                if (right != existingPermission[1])
+                {
+                    vals = [dbChangeUser, datetime.date(), right];
+                    newWhere("AB_PERMISSIONCALENDAR.AB_PERMISSIONCALENDARID", existingPermission[0])
+                        .updateData(true, "AB_PERMISSIONCALENDAR", updateCalCols, updateCalTypes, vals);
+                }
+            }
+            else
+            {
+                vals = [util.getNewUUID(), datetime.date(), dbChangeUser, exchangeUserMap[qualified], $KeywordRegistry.permissionCalendarType$user(), 
+                    exchangeUserMap[owner], $KeywordRegistry.permissionCalendarType$user(), right];
+                db.insertData("AB_PERMISSIONCALENDAR", insertCalCols, insertCalTypes, vals);
+            }
+        }
+    }
+};
+
+
+/**
+ * Reads a <pre><permissions></pre> node from the ews client plugin result (for calendar permissions) and transforms the exchange grant into the grant value
+ * of ADITO
+ * The result of the plugin always contains 2 elements, one for read and one for write
+ *
+ *
+ * @param {XML} pPermissionXML          <p/> XML snippet that was returned by the EWS client plugin call
+ *                                      
+ * @return {String}                     <p/> Permission value that is used within PermissionCalendar-module (@see PermissionCalendar_lib)
+ * @static 
+ */
+EwsClientCalendarPermissionUtils._getCalendarPermissionFromResult = function (pPermissionXML)
+{
+    var permissions = pPermissionXML.permission;
+    var readPermission = false;
+    var writePermission = false;
+    
+    /*
+     The permission.value is on of these:
+     NO_WRITE, WRITE, READ, NO_READ , TimeOnly, TimeAndSubjectAndLocation
+     There are always two entires delivered from the plugin that contain any of these values (one for read, one for write)
+     */
+    for each( var permission in permissions )//XML objects need a "for each (x in y)" loop and not a "for (x in y)" loop
+    {
+        var inputValue = permission.value;
+        /* Overview of the values that are retrieved by the EWS plugin:
+         * "FullDetails" is returned if you've got the grants for:
+         * - Full details
+         * - Editor
+         * - Delegage
+         * "Timeonly" is returned if the grant is "Availability only"
+         * "TimeAndSubjectAndLocation" is returned if the grant is "Limited details"
+         */
+        if (inputValue == "TimeOnly")
+            return $PermissionCalendarCategories.freeBusy();
+        else if(inputValue == "TimeAndSubjectAndLocation")
+            return  $PermissionCalendarCategories.limitedRead();
+        else if( inputValue == "READ" || inputValue == "FullDetails")
+        {
+            readPermission = true;
+        }
+        else if( "WRITE" == inputValue )
+        {
+            writePermission = true;
+        }
+    }
+    var retVal = $PermissionCalendarCategories.nothing();
+    if (readPermission && writePermission)
+        retVal = $PermissionCalendarCategories.readWrite();
+    else if (readPermission && !writePermission)
+        retVal = $PermissionCalendarCategories.fullRead();
+    else if (!readPermission && writePermission)//write but no read has no representator in adito currently
+        throw new Error("EwsClientCalendarPermissionUtils._getCalendarPermissionFromResult: Error while determining the calendar permission; "
+            + "there exists a write but no read permission");
+
+    return retVal;
+}
\ No newline at end of file
diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js
index bd18dea7a2..e8e257f78f 100644
--- a/process/KeywordRegistry_basic/process.js
+++ b/process/KeywordRegistry_basic/process.js
@@ -248,10 +248,9 @@ $KeywordRegistry.workflowSignalTrigger$create = function(){return "TRIGGEREVENTI
 $KeywordRegistry.workflowSignalTrigger$update = function(){return "TRIGGEREVENTUPDATE";};
 $KeywordRegistry.workflowSignalTrigger$delete = function(){return "TRIGGEREVENTDELETE";};
 
-$KeywordRegistry.permissionCalendarCategory = function(){return "PermissionCalendarCategory";};
-$KeywordRegistry.permissionCalendarCategory$readwrite = function(){return "READWRITE";};
-$KeywordRegistry.permissionCalendarCategory$nothing = function(){return "NOTHING";};
 $KeywordRegistry.permissionCalendarType = function(){return "PermissionCalendarType";};
+$KeywordRegistry.permissionCalendarType$user = function(){return "USER";};
+$KeywordRegistry.permissionCalendarType$department = function(){return "DEPARTMENT";};
 
 $KeywordRegistry.workflowActivityType = function(){return "WorkflowActivityType";};
 $KeywordRegistry.workflowActivityType$sequenceFlow = function(){return "sequenceFlow";};
diff --git a/process/PermissionCalendar_lib/PermissionCalendar_lib.aod b/process/PermissionCalendar_lib/PermissionCalendar_lib.aod
index 2bb834eb6e..8f5c6970ce 100644
--- a/process/PermissionCalendar_lib/PermissionCalendar_lib.aod
+++ b/process/PermissionCalendar_lib/PermissionCalendar_lib.aod
@@ -3,6 +3,7 @@
   <name>PermissionCalendar_lib</name>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <process>%aditoprj%/process/PermissionCalendar_lib/process.js</process>
+  <alias>Data_alias</alias>
   <variants>
     <element>LIBRARY</element>
   </variants>
diff --git a/process/PermissionCalendar_lib/process.js b/process/PermissionCalendar_lib/process.js
index dd64cacccb..453eced128 100644
--- a/process/PermissionCalendar_lib/process.js
+++ b/process/PermissionCalendar_lib/process.js
@@ -1,3 +1,8 @@
+import("system.vars");
+import("system.util");
+import("system.db");
+import("system.project");
+import("system.plugin");
 import("system.translate");
 import("system.logging");
 import("AttributeRegistry_basic");
@@ -8,23 +13,25 @@ import("KeywordRegistry_basic");
 import("system.tools");
 import("system.calendars")
 
+/**
+ * provides static functionality for categories of calendar permissions
+ * 
+ * @class
+ */
+function $PermissionCalendarCategories(){}
 
-function PermissionCalendar() {}
+$PermissionCalendarCategories.nothing = function (){return "NOTHING";};
+$PermissionCalendarCategories.freeBusy = function (){return "FREEBUSY";};
+$PermissionCalendarCategories.limitedRead = function (){return "LIMITEDREAD";};
+$PermissionCalendarCategories.fullRead = function (){return "FULLREAD";};
+$PermissionCalendarCategories.readWrite = function (){return "READWRITE";};
 
-PermissionCalendar.objectTypeDepartment = function()
-{
-    return "DEPARTMENT";
-}
-
-PermissionCalendar.objectTypeUser = function()
-{
-    return "USER";
-}
+function PermissionCalendar(){}
 
 PermissionCalendar.getAlias = function()
 {
     return "Data_alias";
-}
+};
 
 /**
  * Returns the permission which is set to those without permissions
@@ -33,33 +40,85 @@ PermissionCalendar.getAlias = function()
  */
 PermissionCalendar.getDefaultPermission = function()
 {
-    return $KeywordRegistry.permissionCalendarCategory$nothing();
-}
+    return $PermissionCalendarCategories.nothing();
+};
+
+
+/**
+ * Returns the ids of categories that may be set for a specific backendType
+ * 
+ * @param {bool} [pBackendType=current calendar backend] <p/> type as constant of calendars.BACKEND_***; e.g. calendars.BACKEND_EXCHANGEWS
+ * 
+ * @return {Array} List of the category ids in the way they are returned by $PermissionCalendarCategories.***, e.g. "NOTHING"
+ */
+PermissionCalendar.getSetableCategories = function(pBackendType)
+{
+    var calendarBackendType = pBackendType || calendars.getBackendType();
+    //different setable categories may be returned here for different backendtypes
+    //for example for Lotus Domino or Microsoft Exchange
+    
+    //default (ADITO DB Calendar Backend) is currently the same as MS Exchange:
+    return [$PermissionCalendarCategories.nothing(),
+            $PermissionCalendarCategories.freeBusy(),
+            $PermissionCalendarCategories.limitedRead(),
+            $PermissionCalendarCategories.fullRead(),
+            $PermissionCalendarCategories.readWrite()];
+};
 
 /**
  * Returns the categorys with the corresponding fields. If fields is null, every field is allowed to be read.
  */
-PermissionCalendar.getCategories = function()
+PermissionCalendar.getCategories = function(pBackendType)
 {
+    var calendarBackendType = pBackendType || calendars.getBackendType();
+    
     var categories = [];
-    categories["NOTHING"] = {};
-    categories["NOTHING"].fields = [];
-    categories["NOTHING"].writeable = false;
+    categories[$PermissionCalendarCategories.nothing()] = {};
+    categories[$PermissionCalendarCategories.nothing()].fields = [];
+    categories[$PermissionCalendarCategories.nothing()].writeable = false;
+    categories[$PermissionCalendarCategories.nothing()].getTitle = function(){
+        return translate.text("No permissions");
+    };
+    
+    categories[$PermissionCalendarCategories.freeBusy()] = {};
+    categories[$PermissionCalendarCategories.freeBusy()].fields = [calendars.ATTENDEES, calendars.DTSTART, calendars.DTEND, calendars.DUE, calendars.DURATION];
+    categories[$PermissionCalendarCategories.freeBusy()].writeable = false;
+    categories[$PermissionCalendarCategories.freeBusy()].getTitle = function(){
+        if (calendarBackendType == calendars.BACKEND_EXCHANGEWS)
+            return translate.text("Availability only");
+        return translate.text("Free or Busy Permission");
+    };
 
-    categories["LIMITEDREAD"] = {};
-    categories["LIMITEDREAD"].fields = [calendars.ATTENDEES, calendars.DTSTART, calendars.DTEND, calendars.DUE, calendars.DURATION];
-    categories["LIMITEDREAD"].writeable = false;
+    categories[$PermissionCalendarCategories.limitedRead()] = {};
+    categories[$PermissionCalendarCategories.limitedRead()].fields = [calendars.ATTENDEES, calendars.DTSTART, calendars.DTEND, calendars.DUE, calendars.DURATION,
+            calendars.SUMMARY, calendars.LOCATION];
+    categories[$PermissionCalendarCategories.limitedRead()].writeable = false;
+    categories[$PermissionCalendarCategories.limitedRead()].getTitle = function(){
+        if (calendarBackendType == calendars.BACKEND_EXCHANGEWS)
+            return translate.text("Limited details");
+        return translate.text("Limited Read Permission");
+    };
 
-    categories["FULLREAD"] = {};
-    categories["FULLREAD"].fields = null;
-    categories["FULLREAD"].writeable = false;
+    categories[$PermissionCalendarCategories.fullRead()] = {};
+    categories[$PermissionCalendarCategories.fullRead()].fields = null;
+    categories[$PermissionCalendarCategories.fullRead()].writeable = false;
+    categories[$PermissionCalendarCategories.fullRead()].getTitle = function(){
+        if (calendarBackendType == calendars.BACKEND_EXCHANGEWS)
+            return translate.text("Full details");
+        return translate.text("Full Read Permission");
+    };
 
-    categories["READWRITE"] = {};
-    categories["READWRITE"].fields = null;
-    categories["READWRITE"].writeable = true;
+    categories[$PermissionCalendarCategories.readWrite()] = {};
+    categories[$PermissionCalendarCategories.readWrite()].fields = null;
+    categories[$PermissionCalendarCategories.readWrite()].writeable = true;
+    categories[$PermissionCalendarCategories.readWrite()].getTitle = function(){
+        if (calendarBackendType == calendars.BACKEND_EXCHANGEWS)
+            return translate.text("Editor");
+        return translate.text("Read and Write Permission");
+    };
     
     return categories;
-}
+};
 
 /**
  * Returns the objecttype of the passed rowId. 
@@ -71,10 +130,10 @@ PermissionCalendar.getCategories = function()
 PermissionCalendar.getObjectType = function(pObjectRowId)
 {
     if(pObjectRowId.length > 36)
-        return PermissionCalendar.objectTypeUser();
+        return $KeywordRegistry.permissionCalendarType$user();
     else 
-        return PermissionCalendar.objectTypeDepartment();    
-}
+        return $KeywordRegistry.permissionCalendarType$department();    
+};
 
 /**
  * Returns for a permission-value the responding view-value.
@@ -84,8 +143,12 @@ PermissionCalendar.getObjectType = function(pObjectRowId)
  */
 PermissionCalendar.getPermissionDisplay = function(pPermissionId)
 {
-    return KeywordUtils.getViewValue($KeywordRegistry.permissionCalendarCategory(), pPermissionId.trim());
-}
+    var properties = PermissionCalendar.getCategories(null)[pPermissionId.trim()];
+    if (!properties)
+        return pPermissionId;
+    var title = properties.getTitle();
+    return title;
+};
 
 /**
  * Returns the display-value for a rowId. Attention: If the type is User, the rowId must be without the prefix.
@@ -96,7 +159,7 @@ PermissionCalendar.getPermissionDisplay = function(pPermissionId)
  */
 PermissionCalendar.getNameDisplay = function(pRowId, pType)
 {
-    if(pType == PermissionCalendar.objectTypeUser())
+    if(pType == $KeywordRegistry.permissionCalendarType$user())
     {
         let user = tools.getUserByAttribute(tools.NAME, EmployeeUtils.prefixUserId(pRowId), tools.PROFILE_TITLE);
         if(user != null)
@@ -104,7 +167,7 @@ PermissionCalendar.getNameDisplay = function(pRowId, pType)
         else
             return "undefined";
     }
-    else if(pType == PermissionCalendar.objectTypeDepartment())
+    else if(pType == $KeywordRegistry.permissionCalendarType$department())
     {
         let dept = new SqlBuilder().select("AB_Attribute.ATTRIBUTE_NAME")
         .from("AB_Attribute")
@@ -119,7 +182,7 @@ PermissionCalendar.getNameDisplay = function(pRowId, pType)
     } 
     else
         return "undefined";
-}
+};
 
 /**
  * Checks, if an permission for the passed combination of parameters exists.
@@ -142,7 +205,7 @@ PermissionCalendar.isPermissionExisting = function(pPermissionDealer_RowId, pPer
         return true;
     else 
         return false;
-}
+};
 
 /**
  * Sets the permissions of the passed user. It is highly recommend to use this function for the autostart.
@@ -156,7 +219,7 @@ PermissionCalendar.setPermissions = function(pUserId)
     let tree = PermissionCalendar.getTree(pUserId);
     tree.forEach(function(item, i)
     {
-        if(item[1] == PermissionCalendar.objectTypeUser())
+        if(item[1] == $KeywordRegistry.permissionCalendarType$user())
         {
             if(!res[item[3]])
             {
@@ -181,7 +244,7 @@ PermissionCalendar.setPermissions = function(pUserId)
     }
     // User itself can read and write his own calendar
     calendars.addPermissions([EmployeeUtils.getCurrentUserName()], calendars.VEVENT, ["READ", "WRITE"], categories["READWRITE"].fields , false, calendars.SORTSTRATEGY_NATURAL);
-}
+};
 
 /**
  * Builds the tree with all departments, users and permissions for the passed userId.
@@ -201,7 +264,7 @@ PermissionCalendar.getTree = function(pCurrentUser)
     
     return resultArr;
     resultArr = [];
-}
+};
 
 /**
  * Get all Permissions of the Departments and Users recursive
@@ -223,7 +286,7 @@ PermissionCalendar._getDataRecursive = function(pRoot, pCurrentUser, pPermission
     {
         PermissionCalendar._getDataRecursive(item, pCurrentUser, pPermissions, pUser, pResultArr);
     });
-}
+};
 
 /**
  * Checks the permissions of the User and whether the user inherits permissions
@@ -256,12 +319,12 @@ PermissionCalendar._createRowUser = function(pDept, inheritedPermission, pCurren
                 }
                 if(flag)
                 {
-                    pResultArr.push([user[0], PermissionCalendar.objectTypeUser(), user[1], inheritedPermission]);
+                    pResultArr.push([user[0], $KeywordRegistry.permissionCalendarType$user(), user[1], inheritedPermission]);
                 }
             }
         }
     });
-}
+};
 
 /**
  * Get the root-Department with the permissions
@@ -283,7 +346,7 @@ PermissionCalendar._getDepartment = function(pAttributeId, pPermissions, pResult
     // If there is no permission set for the Root-deparment, then the default-permission shoud be used
     let inherited = PermissionCalendar.getDefaultPermission();
     return PermissionCalendar._createRowDept(d, inherited, pPermissions, pResultArr);        
-}
+};
 
 /**
  * Returns all subordinate departments of pParentId
@@ -304,7 +367,7 @@ PermissionCalendar._getDepartments = function(pParentId, inheritedPermission, pP
     .table();
     
     return PermissionCalendar._createRowDept(dept, inheritedPermission, pPermissions, pResultArr);
-}
+};
 
 /**
  * Checks the Permissions of the Departments and whether the departments inherits permissions
@@ -333,12 +396,12 @@ PermissionCalendar._createRowDept = function(pTable, inheritedPermission, pPermi
         }
         if(!flag)
         {
-            temp.push([pTable[i][0], PermissionCalendar.objectTypeDepartment(), pTable[i][2], inheritedPermission]);
+            temp.push([pTable[i][0], $KeywordRegistry.permissionCalendarType$department(), pTable[i][2], inheritedPermission]);
         }
     }
     pResultArr = pResultArr.concat(temp);
     return temp;
-} 
+};
 
 /**
  * Returns all permisisons of the passed user.
@@ -354,7 +417,7 @@ PermissionCalendar.getPermissions = function(pCurrentUser)
     .where("AB_PERMISSIONCALENDAR.PERMISSIONPROCURER_ROWID", pCurrentUser)
     .orderBy("PERMISSIONDEALER_ROWID ASC")
     .table();
-}
+};
 
 /*
  * Returns all active users
@@ -370,7 +433,7 @@ PermissionCalendar._getUser = function()
         user[tools.PARAMS].department
         ];
     });
-}
+};
 
 /*
  * Returns all departments over the User
@@ -394,7 +457,5 @@ PermissionCalendar.getAllParents = function(pUserId)
         .arrayColumn()[0];
     }
     
-    logging.log(JSON.stringify(departments));
     return departments;
-    
-}
+};
\ No newline at end of file
diff --git a/process/ewsSyncCalendarPermissions_serverProcess/ewsSyncCalendarPermissions_serverProcess.aod b/process/ewsSyncCalendarPermissions_serverProcess/ewsSyncCalendarPermissions_serverProcess.aod
new file mode 100644
index 0000000000..703deab522
--- /dev/null
+++ b/process/ewsSyncCalendarPermissions_serverProcess/ewsSyncCalendarPermissions_serverProcess.aod
@@ -0,0 +1,11 @@
+<?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>ewsSyncCalendarPermissions_serverProcess</name>
+  <title>Synchronize calendar permissions from Exchange to ADITO</title>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <process>%aditoprj%/process/ewsSyncCalendarPermissions_serverProcess/process.js</process>
+  <alias>Data_alias</alias>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/ewsSyncCalendarPermissions_serverProcess/process.js b/process/ewsSyncCalendarPermissions_serverProcess/process.js
new file mode 100644
index 0000000000..21565f9c37
--- /dev/null
+++ b/process/ewsSyncCalendarPermissions_serverProcess/process.js
@@ -0,0 +1,3 @@
+import("EwsClient_lib");
+
+EwsClientCalendarPermissionUtils.writePermissions("Exchange_devIntern");
\ No newline at end of file
-- 
GitLab