From 546a3870dd4f85cf44faaeb66a912123a7bffe9e Mon Sep 17 00:00:00 2001
From: Johannes Hoermann <j.hoermann@adito.de>
Date: Wed, 12 Jun 2019 15:57:43 +0200
Subject: [PATCH] remove logging

---
 .../entityfields/deleteseries/stateProcess.js |   1 -
 .../recordcontainers/jdito/contentProcess.js  |   1 -
 .../entityfields/icon/valueProcess.js         |   1 -
 .../entityfields/contact_id/valueProcess.js   |   1 -
 .../entityfields/object_type/valueProcess.js  |   1 -
 .../recordcontainers/jdito/contentProcess.js  | 291 ++++---
 .../recordcontainers/jdito/contentProcess.js  |   1 -
 process/ActivityTask_lib/process.js           |   2 -
 process/Context_lib/process.js                |   1 -
 process/DocumentTemplate_lib/process.js       | 736 +++++++++---------
 process/Email_lib/process.js                  | 370 +++++----
 process/KeywordData_lib/process.js            |   2 -
 process/Keyword_lib/process.js                |   6 -
 process/Turnover_lib/process.js               |   4 -
 14 files changed, 697 insertions(+), 721 deletions(-)

diff --git a/entity/Appointment_entity/entityfields/deleteseries/stateProcess.js b/entity/Appointment_entity/entityfields/deleteseries/stateProcess.js
index 8d6b8cb3ec..5b986b8804 100644
--- a/entity/Appointment_entity/entityfields/deleteseries/stateProcess.js
+++ b/entity/Appointment_entity/entityfields/deleteseries/stateProcess.js
@@ -1,5 +1,4 @@
 import("system.tools");
-import("system.logging");
 import("system.vars");
 import("system.result");
 import("system.calendars");
diff --git a/entity/Context_entity/recordcontainers/jdito/contentProcess.js b/entity/Context_entity/recordcontainers/jdito/contentProcess.js
index 17ad5303c6..724a3ee8aa 100644
--- a/entity/Context_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/Context_entity/recordcontainers/jdito/contentProcess.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.vars");
 import("system.result");
 import("Context_lib");
diff --git a/entity/Member_entity/entityfields/icon/valueProcess.js b/entity/Member_entity/entityfields/icon/valueProcess.js
index a19d7b291a..71433b6fd1 100644
--- a/entity/Member_entity/entityfields/icon/valueProcess.js
+++ b/entity/Member_entity/entityfields/icon/valueProcess.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.vars");
 import("system.result");
 import("system.neon");
diff --git a/entity/Offer_entity/entityfields/contact_id/valueProcess.js b/entity/Offer_entity/entityfields/contact_id/valueProcess.js
index 1fca2af19c..b863ff7acb 100644
--- a/entity/Offer_entity/entityfields/contact_id/valueProcess.js
+++ b/entity/Offer_entity/entityfields/contact_id/valueProcess.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.neon");
 import("system.db");
 import("system.result");
diff --git a/entity/Offer_entity/entityfields/object_type/valueProcess.js b/entity/Offer_entity/entityfields/object_type/valueProcess.js
index 93f98305df..2ef4eb50b1 100644
--- a/entity/Offer_entity/entityfields/object_type/valueProcess.js
+++ b/entity/Offer_entity/entityfields/object_type/valueProcess.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.neon");
 import("system.vars");
 import("system.result");
diff --git a/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js b/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
index 055d366a91..44edb699f0 100644
--- a/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/PermissionDetail_entity/recordcontainers/jdito/contentProcess.js
@@ -1,147 +1,146 @@
-import("system.logging");
-import("system.util");
-import("system.vars");
-import("system.db");
-import("system.result");
-import("Permission_lib");
-import("system.project");
-
-var selectedPermission = vars.get("$local.idvalues");
-var sqlStr;
-var whereCond = " where";
-var alias = "_____SYSTEMALIAS";
-var entitiesMetaData = project.getDataModels(project.DATAMODEL_KIND_ENTITY);
-var entityStructure;
-var entitiesUsePermFlagSet = [];
-var fieldsUsePermFlagSet = [];
-
-// gets all names of the entites which have the 'usePermission'-flag set (positive list)
-// gets all names of the fields which have the 'usePermission'-flag set (positive list)
-for each (let entityMetaData in entitiesMetaData) {
-    if (entityMetaData[6] == "true") {
-        entitiesUsePermFlagSet.push(entityMetaData[0])
-        entityStructure = project.getEntityStructure(entityMetaData[0]);
-        for (fieldname in entityStructure.fields) {
-            field = entityStructure.fields[fieldname];
-            if (field.usePermissions == true) {
-                fieldsUsePermFlagSet.push(field.name);
-            }
-        }
-    }   
-}
-
-whereCond += " ENTITY_ID in ('" + entitiesUsePermFlagSet.join("','") + "')";
-whereCond += " and (FIELD_ID in ('" + fieldsUsePermFlagSet.join("','") + "') or FIELD_ID is NULL)";
-
-if (vars.exists("$param.RoleTitle_param") && vars.get("$param.RoleTitle_param")) {
-    whereCond += " and ASYS_PERMISSIONSET.ROLE_ID = '" + vars.getString("$param.RoleTitle_param") + "'";
-} else if (vars.exists("$param.EntityTitle_param") && vars.get("$param.EntityTitle_param")) {
-    whereCond += " and ASYS_PERMISSIONSET.ENTITY_ID = '" + vars.getString("$param.EntityTitle_param") + "'";
-}
-
-sqlStr = 
-    "select ASYS_PERMISSION.ASYS_PERMISSIONID, ASYS_PERMISSIONSET.ENTITY_ID, ASYS_PERMISSIONSET.ROLE_ID,"
-    + " ASYS_PERMISSIONSET.FIELD_ID, ASYS_PERMISSION.COND, ASYS_PERMISSIONACTION.ACTION, ASYS_PERMISSIONSET.ACCESSTYPE, ASYS_PERMISSION.CONDTYPE from ASYS_PERMISSIONSET"
-    + " join ASYS_PERMISSION on ASYS_PERMISSION.ASYS_PERMISSIONSET_ID = ASYS_PERMISSIONSET.ASYS_PERMISSIONSETID"
-    + " join ASYS_PERMISSIONACTION on ASYS_PERMISSIONACTION.ASYS_PERMISSION_ID = ASYS_PERMISSION.ASYS_PERMISSIONID"
-    + whereCond
-    + " order by ASYS_PERMISSION.ASYS_PERMISSIONID";
-
-var sqlRes = db.table(sqlStr, alias);
-var permissionTable = PermissionUtil.convertArrToObj(sqlRes);
-
-// group all permissions by permissionid and condition, concat actions
-var groupedPermissionTable = [], concatAction;
-for (let i = 0; i < permissionTable.length - 1; i++) {
-    for (let j = i + 1; j < permissionTable.length; j++) {
-        if (permissionTable[i].permissionid == permissionTable[j].permissionid && permissionTable[i].cond == permissionTable[j].cond) {
-            var currPermId = permissionTable[i].permissionid;
-            var indexCurrPermGrouped = PermissionUtil.indexOfPermId(groupedPermissionTable, currPermId);
-            if (indexCurrPermGrouped > -1) {
-                // permissionset got already grouped before
-                // concat current action with the actions which got already grouped
-                concatAction = groupedPermissionTable[indexCurrPermGrouped].action + "," + permissionTable[j].action;
-                groupedPermissionTable[indexCurrPermGrouped].action = concatAction;
-                break;
-            } else {
-                concatAction = permissionTable[i].action + "," + permissionTable[j].action;
-                groupedPermissionTable.push(permissionTable[i]);
-                groupedPermissionTable[groupedPermissionTable.length-1].action = concatAction;
-                break;
-            }
-        }
-    }
-}
-
-var res = [];
-var permissionTableOrigin = PermissionUtil.convertArrToObj(sqlRes);
-
-// no permission selected, return all permission entrys
-if (selectedPermission == null) {
-    for each (let entry in groupedPermissionTable) {
-        res = prepareResultArray(entry, res);
-    }
-
-    for each (let entry in permissionTableOrigin) {
-        if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
-            res = prepareResultArray(entry, res);
-        }
-    }
-} else { // permission selected, return only the selected permission entry
-    for each (let entry in groupedPermissionTable) {
-        if (selectedPermission == entry.permissionid) {
-            res = prepareResultArray(entry, res);
-            break;
-        }
-    }
-
-    for each (let entry in permissionTableOrigin) {
-        if (selectedPermission == entry.permissionid) {
-            if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
-                res = prepareResultArray(entry, res);
-                break;
-            }
-        }
-    }
-}
-
-result.object(res.sort(sortFunction));
-
-function prepareResultArray(pEntry, pRes) {
-    var rootPermission = "";
-    if (pEntry.accesstype != "E") {
-        if (pEntry.accesstype == "F" && pEntry.cond != "") {
-            rootPermission = PermissionUtil.getRootFieldPermission(pEntry.permissionid);
-            if (rootPermission == "") {
-                rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);
-            }
-        } else 
-            rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);        
-    }
-    pRes.push([pEntry.permissionid, pEntry.entity, pEntry.role, pEntry.field, pEntry.cond, pEntry.action, pEntry.accesstype, pEntry.condtype, rootPermission]);
-    return pRes;
-}
-
-// used to sort result array: Entity -> Records -> Fields
-function sortFunction(a, b) {
-    if (a[6] == b[6] && a[6] != "F" && a[6] != "R")
-        return 0;
-    else if (a[6] == "E")
-        return -1;
-    else if (b[6] == "E")
-        return 1;
-    else if (a[6] == "R" && b[6] == "F")
-        return -1;
-    else if (a[6] == "F" && b[6] == "R")
-        return 1;
-    else if (a[6] == "R" && b[6] == "R" && a[4] == "")
-        return -1;
-    else if (a[6] == "R" && b[6] == "R" && b[4] == "")
-        return 1;
-    else if (a[6] == "F" && b[6] == "F" && a[4] == "")
-        return -1;
-    else if (a[6] == "F" && b[6] == "F" && b[4] == "")
-        return 1;
-    else
-        return 0;
+import("system.util");
+import("system.vars");
+import("system.db");
+import("system.result");
+import("Permission_lib");
+import("system.project");
+
+var selectedPermission = vars.get("$local.idvalues");
+var sqlStr;
+var whereCond = " where";
+var alias = "_____SYSTEMALIAS";
+var entitiesMetaData = project.getDataModels(project.DATAMODEL_KIND_ENTITY);
+var entityStructure;
+var entitiesUsePermFlagSet = [];
+var fieldsUsePermFlagSet = [];
+
+// gets all names of the entites which have the 'usePermission'-flag set (positive list)
+// gets all names of the fields which have the 'usePermission'-flag set (positive list)
+for each (let entityMetaData in entitiesMetaData) {
+    if (entityMetaData[6] == "true") {
+        entitiesUsePermFlagSet.push(entityMetaData[0])
+        entityStructure = project.getEntityStructure(entityMetaData[0]);
+        for (fieldname in entityStructure.fields) {
+            field = entityStructure.fields[fieldname];
+            if (field.usePermissions == true) {
+                fieldsUsePermFlagSet.push(field.name);
+            }
+        }
+    }   
+}
+
+whereCond += " ENTITY_ID in ('" + entitiesUsePermFlagSet.join("','") + "')";
+whereCond += " and (FIELD_ID in ('" + fieldsUsePermFlagSet.join("','") + "') or FIELD_ID is NULL)";
+
+if (vars.exists("$param.RoleTitle_param") && vars.get("$param.RoleTitle_param")) {
+    whereCond += " and ASYS_PERMISSIONSET.ROLE_ID = '" + vars.getString("$param.RoleTitle_param") + "'";
+} else if (vars.exists("$param.EntityTitle_param") && vars.get("$param.EntityTitle_param")) {
+    whereCond += " and ASYS_PERMISSIONSET.ENTITY_ID = '" + vars.getString("$param.EntityTitle_param") + "'";
+}
+
+sqlStr = 
+    "select ASYS_PERMISSION.ASYS_PERMISSIONID, ASYS_PERMISSIONSET.ENTITY_ID, ASYS_PERMISSIONSET.ROLE_ID,"
+    + " ASYS_PERMISSIONSET.FIELD_ID, ASYS_PERMISSION.COND, ASYS_PERMISSIONACTION.ACTION, ASYS_PERMISSIONSET.ACCESSTYPE, ASYS_PERMISSION.CONDTYPE from ASYS_PERMISSIONSET"
+    + " join ASYS_PERMISSION on ASYS_PERMISSION.ASYS_PERMISSIONSET_ID = ASYS_PERMISSIONSET.ASYS_PERMISSIONSETID"
+    + " join ASYS_PERMISSIONACTION on ASYS_PERMISSIONACTION.ASYS_PERMISSION_ID = ASYS_PERMISSION.ASYS_PERMISSIONID"
+    + whereCond
+    + " order by ASYS_PERMISSION.ASYS_PERMISSIONID";
+
+var sqlRes = db.table(sqlStr, alias);
+var permissionTable = PermissionUtil.convertArrToObj(sqlRes);
+
+// group all permissions by permissionid and condition, concat actions
+var groupedPermissionTable = [], concatAction;
+for (let i = 0; i < permissionTable.length - 1; i++) {
+    for (let j = i + 1; j < permissionTable.length; j++) {
+        if (permissionTable[i].permissionid == permissionTable[j].permissionid && permissionTable[i].cond == permissionTable[j].cond) {
+            var currPermId = permissionTable[i].permissionid;
+            var indexCurrPermGrouped = PermissionUtil.indexOfPermId(groupedPermissionTable, currPermId);
+            if (indexCurrPermGrouped > -1) {
+                // permissionset got already grouped before
+                // concat current action with the actions which got already grouped
+                concatAction = groupedPermissionTable[indexCurrPermGrouped].action + "," + permissionTable[j].action;
+                groupedPermissionTable[indexCurrPermGrouped].action = concatAction;
+                break;
+            } else {
+                concatAction = permissionTable[i].action + "," + permissionTable[j].action;
+                groupedPermissionTable.push(permissionTable[i]);
+                groupedPermissionTable[groupedPermissionTable.length-1].action = concatAction;
+                break;
+            }
+        }
+    }
+}
+
+var res = [];
+var permissionTableOrigin = PermissionUtil.convertArrToObj(sqlRes);
+
+// no permission selected, return all permission entrys
+if (selectedPermission == null) {
+    for each (let entry in groupedPermissionTable) {
+        res = prepareResultArray(entry, res);
+    }
+
+    for each (let entry in permissionTableOrigin) {
+        if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
+            res = prepareResultArray(entry, res);
+        }
+    }
+} else { // permission selected, return only the selected permission entry
+    for each (let entry in groupedPermissionTable) {
+        if (selectedPermission == entry.permissionid) {
+            res = prepareResultArray(entry, res);
+            break;
+        }
+    }
+
+    for each (let entry in permissionTableOrigin) {
+        if (selectedPermission == entry.permissionid) {
+            if (PermissionUtil.indexOfPermId(PermissionUtil.convertArrToObj(res), entry.permissionid) == -1) {
+                res = prepareResultArray(entry, res);
+                break;
+            }
+        }
+    }
+}
+
+result.object(res.sort(sortFunction));
+
+function prepareResultArray(pEntry, pRes) {
+    var rootPermission = "";
+    if (pEntry.accesstype != "E") {
+        if (pEntry.accesstype == "F" && pEntry.cond != "") {
+            rootPermission = PermissionUtil.getRootFieldPermission(pEntry.permissionid);
+            if (rootPermission == "") {
+                rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);
+            }
+        } else 
+            rootPermission = PermissionUtil.getRootPermission(pEntry.permissionid);        
+    }
+    pRes.push([pEntry.permissionid, pEntry.entity, pEntry.role, pEntry.field, pEntry.cond, pEntry.action, pEntry.accesstype, pEntry.condtype, rootPermission]);
+    return pRes;
+}
+
+// used to sort result array: Entity -> Records -> Fields
+function sortFunction(a, b) {
+    if (a[6] == b[6] && a[6] != "F" && a[6] != "R")
+        return 0;
+    else if (a[6] == "E")
+        return -1;
+    else if (b[6] == "E")
+        return 1;
+    else if (a[6] == "R" && b[6] == "F")
+        return -1;
+    else if (a[6] == "F" && b[6] == "R")
+        return 1;
+    else if (a[6] == "R" && b[6] == "R" && a[4] == "")
+        return -1;
+    else if (a[6] == "R" && b[6] == "R" && b[4] == "")
+        return 1;
+    else if (a[6] == "F" && b[6] == "F" && a[4] == "")
+        return -1;
+    else if (a[6] == "F" && b[6] == "F" && b[4] == "")
+        return 1;
+    else
+        return 0;
 }
\ No newline at end of file
diff --git a/entity/Turnover_entity/recordcontainers/jdito/contentProcess.js b/entity/Turnover_entity/recordcontainers/jdito/contentProcess.js
index 4bc466e09a..f8be0738cc 100644
--- a/entity/Turnover_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/Turnover_entity/recordcontainers/jdito/contentProcess.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("Turnover_lib");
 import("system.vars");
 import("system.result");
diff --git a/process/ActivityTask_lib/process.js b/process/ActivityTask_lib/process.js
index ed6f8af1ee..174dfc5ff2 100644
--- a/process/ActivityTask_lib/process.js
+++ b/process/ActivityTask_lib/process.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.vars");
 import("system.util");
 import("system.datetime");
@@ -132,7 +131,6 @@ TaskUtils.getTypeAttributes = function(pType)
 
 TaskUtils.getAvailableStatus = function(pType)
 {
-    logging.log("hääää" + KeywordUtils.getAttributeRelation(pType, $KeywordRegistry.taskType(), "availableStatus", ""))
     return JSON.parse(KeywordUtils.getAttributeRelation(pType, $KeywordRegistry.taskType(), "availableStatus", ""));
 }
 
diff --git a/process/Context_lib/process.js b/process/Context_lib/process.js
index 711da7bd3e..8eb5ede48e 100644
--- a/process/Context_lib/process.js
+++ b/process/Context_lib/process.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.db");
 import("system.translate");
 import("system.project");
diff --git a/process/DocumentTemplate_lib/process.js b/process/DocumentTemplate_lib/process.js
index ba6e8e61be..ba8ec78cfb 100644
--- a/process/DocumentTemplate_lib/process.js
+++ b/process/DocumentTemplate_lib/process.js
@@ -1,369 +1,369 @@
-import("Communication_lib");
-import("system.neon");
-import("Employee_lib");
-import("KeywordRegistry_basic");
-import("Document_lib");
-import("KeywordData_lib");
-import("Sql_lib");
-import("Address_lib");
-import("system.process");
-import("system.vars");
-import("system.db");
-import("system.util");
-import("system.pack");
-import("system.fileIO");
-import("system.translate");
-import("system.datetime");
-import("system.text");
-import("system.mail");
-import("Keyword_lib");
-
-/**
- * Object for working with document templates, holds the content and type of the template.
- * Provides functions to replace placeholders in the content.
- */
-function DocumentTemplate (pTemplateContent, pType, pFilename)
-{
-    this.content = pTemplateContent;
-    this.type = pType;
-    this.filename = pFilename;
-}
-
-/**
- * The types a DocumentTemplate can have. Depending on the type,
- * the correct method for replacing the placeholders can be chosen
- */
-DocumentTemplate.types = {
-    TXT : "txt",
-    HTML : "html",
-    EML : "eml",
-    ODT : "odt",
-    DOCX : "docx"
-};
-
-/**
- * Loads the content of a document template and creates a new DocumentTemplate object with that.
- * 
- * @param {String} pTemplateId id of the template
- * 
- * @return {DocumentTemplate} template object
- */
-DocumentTemplate.loadTemplate = function (pTemplateId)
-{
-    var alias = "_____SYSTEMALIAS";
-    var templateDocument = db.getBinaryMetadata("DOCUMENTTEMPLATE", "DOCUMENT", pTemplateId, false, alias, null);
-    if (!templateDocument[0])
-        return new DocumentTemplate();
-    var binaryId = templateDocument[0][db.BINARY_ID];
-    var filename = templateDocument[0][db.BINARY_FILENAME];
-    var type = DocumentUtil.getFileExtensionFromUpload(filename); //TODO: if unknown type, throw error?
-    if (binaryId)
-        return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename);
-    return new DocumentTemplate(); //TODO: throw error?
-}
-
-DocumentTemplate._replaceText = function (pText, pReplacements)
-{
-    for (let placeholder in pReplacements)
-        pText = pText.replace(placeholder, pReplacements[placeholder], "ig");
-    return pText;
-}
-
-DocumentTemplate.prototype.toString = function ()
-{
-    return this.content;
-}
-
-/**
- * Returns the template content with replaced placeholders by choosing the right
- * replace function for the type.
- * 
- * @param {Object} pReplacements map, the structure is {placeholder : value}
- * 
- * @return {String} the replaced content
- */
-DocumentTemplate.prototype.getReplacedContent = function (pReplacements)
-{
-    switch (this.type)
-    {
-        case DocumentTemplate.types.HTML:
-            for (let i in pReplacements)
-                pReplacements[i] = text.replaceAll(pReplacements[i], {"\n" : "<br>"});
-        case DocumentTemplate.types.TXT:
-            let decodedContent = util.decodeBase64String(this.content)
-            return DocumentTemplate._replaceText(decodedContent, pReplacements);
-        case DocumentTemplate.types.EML:
-            return this._getReplacedEML(pReplacements);
-        case DocumentTemplate.types.ODT:
-            return this._getReplacedODT(pReplacements);
-        case DocumentTemplate.types.DOCX:
-            return this._getReplacedDOCX(pReplacements);
-        default:
-            return null; //TODO: throw error
-    }
-}
-
-DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId)
-{
-    var config = PlaceholderUtils.getPlaceholders();
-    var addressData = getAddressesData([pContactId], config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection
-    var replacements = {};
-    for (let i = 0, l = addressData[0].length; i < l; i++)
-    {
-        replacements[addressData[0][i]] = addressData[1][i];
-    }
-    return this.getReplacedContent(replacements);
-}
-
-/**
- * replaces placeholders for EML
- * 
- * @param {Object} pReplacements mapping with replacements for every placeholder
- * 
- * @return {String} the replaced content
- */
-DocumentTemplate.prototype._getReplacedEML = function (pReplacements)
-{
-    var email = mail.parseRFC(util.decodeBase64String(this.content));
-    var htmlText = email[mail.MAIL_HTMLTEXT];
-    return DocumentTemplate._replaceText(htmlText, pReplacements);
-}
-
-/*
- * replaces a given Odt-File on the server and returns the replaced base64-file
- *
- * @param {String|String[]} pAddrDataCondition req a SQL-Condition or an Array of Relation-Ids for reducing the default-placeholders
- * @param {String} pAddressID opt if you want to use the standard address you can pass undefined, otherwise you need to specify a Address-Id
- *
- * @return {String} base64-encoded replaced file
- */
-DocumentTemplate.prototype._getReplacedODT = function (pReplacements)
-{
-    //save the file on the server so it can be unzipped via pack.getFromZip
-    var serverFilePath = vars.get("$sys.servertemp") + "/clientid_" + vars.get("$sys.clientid")
-        + "/" + util.getNewUUID() + "/" + this.filename.replace(/\\/g, "/");
-    
-    fileIO.storeData(serverFilePath, this.content, util.DATA_BINARY, false);
-    if (!_replaceODTFile(pReplacements, serverFilePath))
-        return null;
-
-    var replacedFileData = fileIO.getData(serverFilePath, util.DATA_BINARY);
-    fileIO.remove(serverFilePath);
-
-    return replacedFileData;
-    
-    /*
-    * ersetzt die Platzhalter in ODT-Datei
-    *
-    * @param {String} pCondition req Condition
-    * @param {String} pODTFileName req Filename des odt-Datei
-    * @param {String} pAddressID opt ID von der die Adressdaten geholt werden
-    *
-    * @return {Boolean}
-    */
-    function _replaceODTFile (pReplacements, pODTFileName)
-    {
-        var senderRelId = EmployeeUtils.getCurrentContactId();
-        if (senderRelId == null)
-            return false;
-        if (Object.keys(pReplacements).length)
-        {
-            // ersetzen Platzhalter in content.xml
-
-            var textS = util.decodeBase64String(pack.getFromZip(pODTFileName, "content.xml"));
-            var bodybegin = textS.indexOf("<office:body>");
-            var bodyend =  textS.indexOf("</office:body>") + 14;
-            var body = textS.substring( bodybegin, bodyend );
-            var lastbody = textS.substr( bodyend );
-            textS = textS.substring( 0, bodybegin );
-            for (let placeholder in pReplacements)
-            {
-                body = body.replace(placeholder,
-                    pReplacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
-            }
-            textS += body;
-            textS += lastbody;
-            pack.addToZip(pODTFileName, "content.xml", util.encodeBase64String(textS))
-            // ersetzen Platzhalter in styles.xml
-            var styles = util.decodeBase64String(pack.getFromZip(pODTFileName, "styles.xml"));
-            for (let placeholder in pReplacements)
-            {
-                styles = styles.replace(placeholder,
-                    pReplacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
-            }
-            pack.addToZip(pODTFileName, "styles.xml", util.encodeBase64String(styles));
-            return true;
-        }
-        return false;
-    }
-}
-
-/*
- * This function is used to replace placeholders via DocXTemplater
- * 
- * @param {Object} pReplacements - Must contain an object, which holds the placeholders
- * 
- * @return {String} returns the modified document in a BASE64 coded string
- */
-DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements)
-{
-    var replacements = {};
-    for (let placeholder in pReplacements)
-        replacements[placeholder.slice(3, -3)] = pReplacements[placeholder];
-    
-    //this is executed as a process because of better performance
-    var documentData = process.execute("getDocxDocument_serverProcess", {
-        templateb64: this.content,
-        placeholderConfig: JSON.stringify(replacements) //process.execute is only able to handle strings
-    });
-
-    return documentData;
-}
-
-/**
- * Provides functions for document templates
- */
-function DocumentTemplateUtils () {}
-
-/**
- * loads a document template and replaces the placeholders
- * 
- * @param {String} pTemplateId id of the document template
- * @param {String} pContactId the contact whose address data is used
- */
-DocumentTemplateUtils.getSingleReplacedDocument = function (pTemplateId, pContactId)
-{
-    var template = DocumentTemplate.loadTemplate(pTemplateId);
-    return template.getReplacedTextByContactId(pContactId);
-}
-
-/**
- * represents a placeholder
- * 
- * @param {String} pName name of the placeholder (without prefix and postfix)
- * @param {String} pType type of the placeholder, see PlaceholderUtils.types
- * @param {String|Function} pValueDef string or function (depends on the type) defining the value
- * @param {String} [pTarget] what contact is used to get the data, see PlaceholderUtils.targets
- */
-function Placeholder (pName, pType, pValueDef, pTarget) 
-{
-    this.placeholderName = PlaceholderUtils.formatPlaceholder(pName);
-    this.type = pType;
-    this.target = pTarget || Placeholder.targets.RECIPIENT;
-    this.valueDefinition = pValueDef;
-}
-
-
-/**
- * placeholder types, defines how the value is acquired
- */
-Placeholder.types = {
-    ADDRESSFORMAT : "ADDRESSFORMAT",
-    SQLPART : "SQLPART",
-    SQLPARTFUNCTION : "SQLPARTFUNCTION"
-};
-/**
- * placeholder targets, defines whose data is used
- */
-Placeholder.targets = {
-    /**
-     * use the data of the recipient (default)
-     */
-    RECIPIENT : "RECIPIENT",
-    /**
-     * use the data of the sender
-     */
-    SENDER : "SENDER",
-    /**
-     * use the data of the user
-     */
-    EMPLOYEE : "EMPLOYEE"
-};
-
-function PlaceholderUtils () {}
-
-/**
- * Returns the placeholder with the required prefix and postfix added.
- * This function defines the format for placeholders.
- */
-PlaceholderUtils.formatPlaceholder = function (pPlaceholder, pEscapeForRegex)
-{
-    return "{@" + pPlaceholder + "@}";
-}
-
-/**
- * placeholder configuration
- * 
- * @return {Array} array of placeholders
- */
-PlaceholderUtils.getPlaceholders = function () 
-{
-    //these functions should make adding placeholders easier and require less code:
-    
-    /**
-     * add an address format placeholder to placeholders
-     */
-    function _addAddressFormat (pName, pFormat, pTarget)
-    {
-        placeholders.push(new Placeholder(pName, Placeholder.types.ADDRESSFORMAT, pFormat, pTarget));
-    }
-    
-    /**
-     * Add a sub-sql placeholder to placeholders. For further information regarding the full query, you can take
-     * a look at the function 'fetchAddressData' in Address_lib. You can use fields from CONTACT, ADDRESS, ORGANISATION and PERSON.
-     */
-    function _addSqlPart (pName, pSqlPart, pTarget, pAddBraces)
-    {
-        placeholders.push(new Placeholder(pName, Placeholder.types.SQLPART, pAddBraces ? "(" + pSqlPart + ")" : pSqlPart, pTarget));
-    }
-    
-    /**
-     * Add a placeholder to placeholders with a function that returns a sub-sql. The function will be called
-     * with no parameters. You have to deliver an actual function, don't call it here.
-     */
-    function _addSqlPartFunction (pName, pSqlPartFunction, pTarget)
-    {
-        placeholders.push(new Placeholder(pName, Placeholder.types.SQLPARTFUNCTION, pSqlPartFunction, pTarget));
-    }
-    
-    
-    var placeholders = [];
-    
-    //placeholders should be added here:
-    
-    _addAddressFormat("address", "{street} {buildingno}");
-    _addAddressFormat("zipCode", "{zip}");
-    _addAddressFormat("city", "{city}");
-    _addAddressFormat("district", "{district}");
-    _addAddressFormat("region", "{region}");
-    _addAddressFormat("country", "{country}");
-    _addAddressFormat("letterSalutation", "{letter_salutation}");
-    _addAddressFormat("fullAddress", "");
-    _addAddressFormat("senderOrgname", "{organisation_name}", Placeholder.targets.SENDER);
-    _addAddressFormat("senderAddress", "{street} {buildingno}", Placeholder.targets.SENDER);
-    _addAddressFormat("senderZipCity", "{country} - {zip} {city}", Placeholder.targets.SENDER);
-    _addAddressFormat("senderFullAddress", "", Placeholder.targets.SENDER);
-    
-    _addSqlPart("orgname", "ORGANISATION.NAME");
-    
-    _addSqlPartFunction("phone", CommUtil.getStandardSubSqlPhone);
-    _addSqlPartFunction("email", CommUtil.getStandardSubSqlMail);
-    _addSqlPartFunction("senderPhone", CommUtil.getStandardSubSqlPhone, Placeholder.targets.SENDER);
-    _addSqlPartFunction("senderEmail", CommUtil.getStandardSubSqlMail, Placeholder.targets.SENDER);
-    
-    
-    return placeholders;
-}
-
-/**
- * functions for working with letters
- */
-function LetterUtils () {}
-
-LetterUtils.openNewLetter = function (pContactId)
-{
-    var params = {
-        "ContactId_param" : pContactId
-    };
-    neon.openContext("Letter", "LetterEdit_view", null, neon.OPERATINGSTATE_NEW, params);
+import("Communication_lib");
+import("system.neon");
+import("Employee_lib");
+import("KeywordRegistry_basic");
+import("Document_lib");
+import("KeywordData_lib");
+import("Sql_lib");
+import("Address_lib");
+import("system.process");
+import("system.vars");
+import("system.db");
+import("system.util");
+import("system.pack");
+import("system.fileIO");
+import("system.translate");
+import("system.datetime");
+import("system.text");
+import("system.mail");
+import("Keyword_lib");
+
+/**
+ * Object for working with document templates, holds the content and type of the template.
+ * Provides functions to replace placeholders in the content.
+ */
+function DocumentTemplate (pTemplateContent, pType, pFilename)
+{
+    this.content = pTemplateContent;
+    this.type = pType;
+    this.filename = pFilename;
+}
+
+/**
+ * The types a DocumentTemplate can have. Depending on the type,
+ * the correct method for replacing the placeholders can be chosen
+ */
+DocumentTemplate.types = {
+    TXT : "txt",
+    HTML : "html",
+    EML : "eml",
+    ODT : "odt",
+    DOCX : "docx"
+};
+
+/**
+ * Loads the content of a document template and creates a new DocumentTemplate object with that.
+ * 
+ * @param {String} pTemplateId id of the template
+ * 
+ * @return {DocumentTemplate} template object
+ */
+DocumentTemplate.loadTemplate = function (pTemplateId)
+{
+    var alias = "_____SYSTEMALIAS";
+    var templateDocument = db.getBinaryMetadata("DOCUMENTTEMPLATE", "DOCUMENT", pTemplateId, false, alias, null);
+    if (!templateDocument[0])
+        return new DocumentTemplate();
+    var binaryId = templateDocument[0][db.BINARY_ID];
+    var filename = templateDocument[0][db.BINARY_FILENAME];
+    var type = DocumentUtil.getFileExtensionFromUpload(filename); //TODO: if unknown type, throw error?
+    if (binaryId)
+        return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename);
+    return new DocumentTemplate(); //TODO: throw error?
+}
+
+DocumentTemplate._replaceText = function (pText, pReplacements)
+{
+    for (let placeholder in pReplacements)
+        pText = pText.replace(placeholder, pReplacements[placeholder], "ig");
+    return pText;
+}
+
+DocumentTemplate.prototype.toString = function ()
+{
+    return this.content;
+}
+
+/**
+ * Returns the template content with replaced placeholders by choosing the right
+ * replace function for the type.
+ * 
+ * @param {Object} pReplacements map, the structure is {placeholder : value}
+ * 
+ * @return {String} the replaced content
+ */
+DocumentTemplate.prototype.getReplacedContent = function (pReplacements)
+{
+    switch (this.type)
+    {
+        case DocumentTemplate.types.HTML:
+            for (let i in pReplacements)
+                pReplacements[i] = text.replaceAll(pReplacements[i], {"\n" : "<br>"});
+        case DocumentTemplate.types.TXT:
+            let decodedContent = util.decodeBase64String(this.content)
+            return DocumentTemplate._replaceText(decodedContent, pReplacements);
+        case DocumentTemplate.types.EML:
+            return this._getReplacedEML(pReplacements);
+        case DocumentTemplate.types.ODT:
+            return this._getReplacedODT(pReplacements);
+        case DocumentTemplate.types.DOCX:
+            return this._getReplacedDOCX(pReplacements);
+        default:
+            return null; //TODO: throw error
+    }
+}
+
+DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId)
+{
+    var config = PlaceholderUtils.getPlaceholders();
+    var addressData = getAddressesData([pContactId], config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection
+    var replacements = {};
+    for (let i = 0, l = addressData[0].length; i < l; i++)
+    {
+        replacements[addressData[0][i]] = addressData[1][i];
+    }
+    return this.getReplacedContent(replacements);
+}
+
+/**
+ * replaces placeholders for EML
+ * 
+ * @param {Object} pReplacements mapping with replacements for every placeholder
+ * 
+ * @return {String} the replaced content
+ */
+DocumentTemplate.prototype._getReplacedEML = function (pReplacements)
+{
+    var email = mail.parseRFC(util.decodeBase64String(this.content));
+    var htmlText = email[mail.MAIL_HTMLTEXT];
+    return DocumentTemplate._replaceText(htmlText, pReplacements);
+}
+
+/*
+ * replaces a given Odt-File on the server and returns the replaced base64-file
+ *
+ * @param {String|String[]} pAddrDataCondition req a SQL-Condition or an Array of Relation-Ids for reducing the default-placeholders
+ * @param {String} pAddressID opt if you want to use the standard address you can pass undefined, otherwise you need to specify a Address-Id
+ *
+ * @return {String} base64-encoded replaced file
+ */
+DocumentTemplate.prototype._getReplacedODT = function (pReplacements)
+{
+    //save the file on the server so it can be unzipped via pack.getFromZip
+    var serverFilePath = vars.get("$sys.servertemp") + "/clientid_" + vars.get("$sys.clientid")
+        + "/" + util.getNewUUID() + "/" + this.filename.replace(/\\/g, "/");
+    
+    fileIO.storeData(serverFilePath, this.content, util.DATA_BINARY, false);
+    if (!_replaceODTFile(pReplacements, serverFilePath))
+        return null;
+
+    var replacedFileData = fileIO.getData(serverFilePath, util.DATA_BINARY);
+    fileIO.remove(serverFilePath);
+
+    return replacedFileData;
+    
+    /*
+    * ersetzt die Platzhalter in ODT-Datei
+    *
+    * @param {String} pCondition req Condition
+    * @param {String} pODTFileName req Filename des odt-Datei
+    * @param {String} pAddressID opt ID von der die Adressdaten geholt werden
+    *
+    * @return {Boolean}
+    */
+    function _replaceODTFile (pReplacements, pODTFileName)
+    {
+        var senderRelId = EmployeeUtils.getCurrentContactId();
+        if (senderRelId == null)
+            return false;
+        if (Object.keys(pReplacements).length)
+        {
+            // ersetzen Platzhalter in content.xml
+
+            var textS = util.decodeBase64String(pack.getFromZip(pODTFileName, "content.xml"));
+            var bodybegin = textS.indexOf("<office:body>");
+            var bodyend =  textS.indexOf("</office:body>") + 14;
+            var body = textS.substring( bodybegin, bodyend );
+            var lastbody = textS.substr( bodyend );
+            textS = textS.substring( 0, bodybegin );
+            for (let placeholder in pReplacements)
+            {
+                body = body.replace(placeholder,
+                    pReplacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
+            }
+            textS += body;
+            textS += lastbody;
+            pack.addToZip(pODTFileName, "content.xml", util.encodeBase64String(textS))
+            // ersetzen Platzhalter in styles.xml
+            var styles = util.decodeBase64String(pack.getFromZip(pODTFileName, "styles.xml"));
+            for (let placeholder in pReplacements)
+            {
+                styles = styles.replace(placeholder,
+                    pReplacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
+            }
+            pack.addToZip(pODTFileName, "styles.xml", util.encodeBase64String(styles));
+            return true;
+        }
+        return false;
+    }
+}
+
+/*
+ * This function is used to replace placeholders via DocXTemplater
+ * 
+ * @param {Object} pReplacements - Must contain an object, which holds the placeholders
+ * 
+ * @return {String} returns the modified document in a BASE64 coded string
+ */
+DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements)
+{
+    var replacements = {};
+    for (let placeholder in pReplacements)
+        replacements[placeholder.slice(3, -3)] = pReplacements[placeholder];
+    
+    //this is executed as a process because of better performance
+    var documentData = process.execute("getDocxDocument_serverProcess", {
+        templateb64: this.content,
+        placeholderConfig: JSON.stringify(replacements) //process.execute is only able to handle strings
+    });
+
+    return documentData;
+}
+
+/**
+ * Provides functions for document templates
+ */
+function DocumentTemplateUtils () {}
+
+/**
+ * loads a document template and replaces the placeholders
+ * 
+ * @param {String} pTemplateId id of the document template
+ * @param {String} pContactId the contact whose address data is used
+ */
+DocumentTemplateUtils.getSingleReplacedDocument = function (pTemplateId, pContactId)
+{
+    var template = DocumentTemplate.loadTemplate(pTemplateId);
+    return template.getReplacedTextByContactId(pContactId);
+}
+
+/**
+ * represents a placeholder
+ * 
+ * @param {String} pName name of the placeholder (without prefix and postfix)
+ * @param {String} pType type of the placeholder, see PlaceholderUtils.types
+ * @param {String|Function} pValueDef string or function (depends on the type) defining the value
+ * @param {String} [pTarget] what contact is used to get the data, see PlaceholderUtils.targets
+ */
+function Placeholder (pName, pType, pValueDef, pTarget) 
+{
+    this.placeholderName = PlaceholderUtils.formatPlaceholder(pName);
+    this.type = pType;
+    this.target = pTarget || Placeholder.targets.RECIPIENT;
+    this.valueDefinition = pValueDef;
+}
+
+
+/**
+ * placeholder types, defines how the value is acquired
+ */
+Placeholder.types = {
+    ADDRESSFORMAT : "ADDRESSFORMAT",
+    SQLPART : "SQLPART",
+    SQLPARTFUNCTION : "SQLPARTFUNCTION"
+};
+/**
+ * placeholder targets, defines whose data is used
+ */
+Placeholder.targets = {
+    /**
+     * use the data of the recipient (default)
+     */
+    RECIPIENT : "RECIPIENT",
+    /**
+     * use the data of the sender
+     */
+    SENDER : "SENDER",
+    /**
+     * use the data of the user
+     */
+    EMPLOYEE : "EMPLOYEE"
+};
+
+function PlaceholderUtils () {}
+
+/**
+ * Returns the placeholder with the required prefix and postfix added.
+ * This function defines the format for placeholders.
+ */
+PlaceholderUtils.formatPlaceholder = function (pPlaceholder, pEscapeForRegex)
+{
+    return "{@" + pPlaceholder + "@}";
+}
+
+/**
+ * placeholder configuration
+ * 
+ * @return {Array} array of placeholders
+ */
+PlaceholderUtils.getPlaceholders = function () 
+{
+    //these functions should make adding placeholders easier and require less code:
+    
+    /**
+     * add an address format placeholder to placeholders
+     */
+    function _addAddressFormat (pName, pFormat, pTarget)
+    {
+        placeholders.push(new Placeholder(pName, Placeholder.types.ADDRESSFORMAT, pFormat, pTarget));
+    }
+    
+    /**
+     * Add a sub-sql placeholder to placeholders. For further information regarding the full query, you can take
+     * a look at the function 'fetchAddressData' in Address_lib. You can use fields from CONTACT, ADDRESS, ORGANISATION and PERSON.
+     */
+    function _addSqlPart (pName, pSqlPart, pTarget, pAddBraces)
+    {
+        placeholders.push(new Placeholder(pName, Placeholder.types.SQLPART, pAddBraces ? "(" + pSqlPart + ")" : pSqlPart, pTarget));
+    }
+    
+    /**
+     * Add a placeholder to placeholders with a function that returns a sub-sql. The function will be called
+     * with no parameters. You have to deliver an actual function, don't call it here.
+     */
+    function _addSqlPartFunction (pName, pSqlPartFunction, pTarget)
+    {
+        placeholders.push(new Placeholder(pName, Placeholder.types.SQLPARTFUNCTION, pSqlPartFunction, pTarget));
+    }
+    
+    
+    var placeholders = [];
+    
+    //placeholders should be added here:
+    
+    _addAddressFormat("address", "{street} {buildingno}");
+    _addAddressFormat("zipCode", "{zip}");
+    _addAddressFormat("city", "{city}");
+    _addAddressFormat("district", "{district}");
+    _addAddressFormat("region", "{region}");
+    _addAddressFormat("country", "{country}");
+    _addAddressFormat("letterSalutation", "{letter_salutation}");
+    _addAddressFormat("fullAddress", "");
+    _addAddressFormat("senderOrgname", "{organisation_name}", Placeholder.targets.SENDER);
+    _addAddressFormat("senderAddress", "{street} {buildingno}", Placeholder.targets.SENDER);
+    _addAddressFormat("senderZipCity", "{country} - {zip} {city}", Placeholder.targets.SENDER);
+    _addAddressFormat("senderFullAddress", "", Placeholder.targets.SENDER);
+    
+    _addSqlPart("orgname", "ORGANISATION.NAME");
+    
+    _addSqlPartFunction("phone", CommUtil.getStandardSubSqlPhone);
+    _addSqlPartFunction("email", CommUtil.getStandardSubSqlMail);
+    _addSqlPartFunction("senderPhone", CommUtil.getStandardSubSqlPhone, Placeholder.targets.SENDER);
+    _addSqlPartFunction("senderEmail", CommUtil.getStandardSubSqlMail, Placeholder.targets.SENDER);
+    
+    
+    return placeholders;
+}
+
+/**
+ * functions for working with letters
+ */
+function LetterUtils () {}
+
+LetterUtils.openNewLetter = function (pContactId)
+{
+    var params = {
+        "ContactId_param" : pContactId
+    };
+    neon.openContext("Letter", "LetterEdit_view", null, neon.OPERATINGSTATE_NEW, params);
 }
\ No newline at end of file
diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js
index e0e8cdf0ef..dc52ef7627 100644
--- a/process/Email_lib/process.js
+++ b/process/Email_lib/process.js
@@ -1,187 +1,185 @@
-import("system.logging");
-import("system.translate");
-import("system.text");
-import("system.db");
-import("system.util");
-import("Communication_lib");
-import("DocumentTemplate_lib");
-import("system.neon");
-import("system.mail");
-
-function EmailUtils () {}
-
-/**
- * creates a new E-Mail-Object and ask for a download of a eml where all fields are prefilled
- * The eml can be open with a mailclient and sent via the mailclient. Each mailclient has a different behaviour:
- * In Outlook the mail is automatically opened in draft-mode
- * In Thunderbird the mail is opened in view mode and you've to manually "edit as new"
- * 
- * @param {String|Array} pToRecipients mailaddresses of the recipients, can either be a 1D-Array with several addresses or a string with one address
- * @param {String} pSenderContactId contactId of the sender. the standard mailadress of the contact is used as sender-address
- * @param {String} [pTemplateId] if a document-template shall be used, give the templateId here
- * @param {String} [pRecipientContactId] contactId of the recipient, required to fill placeholders
- */
-EmailUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId)
-{
-    var email = new Email(pToRecipients);
-    email.setSender(pSenderContactId);
-    if (pTemplateId)
-        email.setTemplate(pTemplateId, pRecipientContactId);
-
-    email.openMail();
-}
-
-/**
- * opens a view where a new mail can be sent. In the view the use CAN select a DocumentTemplate if needed
- * 
- * @param {String} pToContactId contactId with contacts to filter the communication-addresses
- */
-EmailUtils.openNewMail = function (pToContactId)
-{
-    var params = {
-        "ContactId_param" : pToContactId
-    };
-    neon.openContext("Email", "EmailEdit_view", null, neon.OPERATINGSTATE_NEW, params);
-}
-
-/**
- * object for handling emails
- * 
- * @param {String|Array} [pToRecipients=[]] recipient email address or array of recipient email addresses
- * @param {String} [pSender=null] email address of the sender
- * @param {String} [pSubject=null] subject
- * @param {String} [pBody=null] mail body
- * @param {Array} [pCcRecipients=[]] array of recipient cc addresses
- * @param {Array} [pBccRecipients=[]] array of recipient bcc addresses
- * 
- * @class
- */
-function Email (pToRecipients, pSender, pSubject, pBody, pCcRecipients, pBccRecipients)
-{
-    if (pToRecipients && typeof(pToRecipients) == "string")
-        pToRecipients = [pToRecipients];
-    
-    this.sender = pSender;
-    this.subject = pSubject;
-    this.body = pBody;
-    this.toRecipients = pToRecipients || [];
-    this.ccRecipients = pCcRecipients || [];
-    this.bccRecipients = pBccRecipients || [];
-}
-
-/**
- * loads a document template into the mail body
- * 
- * @param {String} pTemplateId the id of the template
- * @param {String} pContactId the id of the template
- * 
- * @throws {Error} if the type of the template is invalid
- */
-Email.prototype.setTemplate = function (pTemplateId, pContactId)
-{
-    var template = DocumentTemplate.loadTemplate(pTemplateId);
-    
-    //TODO: also set other properties if the template is a eml
-    if (template)
-    {
-        if (template.type != DocumentTemplate.types.EML && template.type != DocumentTemplate.types.HTML && template.type != DocumentTemplate.types.TXT)
-            throw new Error("Invalid document type for an email template");
-        
-        this.body = template.getReplacedContentByContactId(pContactId);
-    }
-}
-
-/**
- * sets the sender of the mail
- * 
- * @param {String} pContactId the contactId of the sender
- */
-Email.prototype.setSender = function (pContactId)
-{
-    this.sender = CommUtil.getStandardMail(pContactId);
-}
-
-/**
- * generates a 'mailto:' URL from the email object
- */
-Email.prototype.getMailtoUrl = function ()
-{
-    var url = [];
-    
-    if (this.toRecipients.length)
-        url.push("to=" + this.toRecipients.join());
-    
-    if (this.ccRecipients.length)
-        url.push("cc=" + this.ccRecipients.join());
-    
-    if (this.bccRecipients.length)
-        url.push("bcc=" + this.bccRecipients.join());
-    
-    if (this.subject)
-        url.push("subject=" + this.subject);
-    
-    if (this.body)
-        url.push("body=" + text.html2text(this.body));
-    
-    url = "mailto:?" + url.join("&");
-    
-    return encodeURI(url);
-}
-
-/**
- * generates a eml-element from the email object
- */
-Email.prototype.getRFCmail = function ()
-{
-    logging.log(this.body)
-    var ENCODING = "UTF-8";
-    var mailId = mail.newMail();
-
-    if (this.toRecipients.length)
-        mail.addRecipients(mailId, mail.RECIPIENT_TO, this.toRecipients);
-
-    if (this.ccRecipients.length)
-        mail.addRecipients(mailId, mail.RECIPIENT_CC, this.ccRecipients);
-
-    if (this.bccRecipients.length)
-        mail.addRecipients(mailId, mail.RECIPIENT_BCC, this.bccRecipients);
-
-    if (this.subject)
-        mail.setSubject(mailId, this.subject, ENCODING);
-
-    if (this.body)
-        mail.addText(mailId, this.body, "text/html", ENCODING, null);
-    else
-        mail.addText(mailId, "", "text/html", ENCODING, null);
-    
-    //"X-Unsent" is a very badly, non-standardised header to gently ask the mail client that the mail should open in a compose-mode
-    //this is mainly done for Microsoft Outlook for Windows. 
-    //Thunderbird has a dinosaur-request (it's from the year 2002) to also support this: https://bugzilla.mozilla.org/show_bug.cgi?id=166541 
-    mail.addHeader(mailId, "X-Unsent", "1");
-    
-    //accoding to this entry: https://stackoverflow.com/questions/11330628/os-x-mail-open-eml-files-in-compose-mode/33224913
-    //something similar exists for OS X Mail
-    //X-Uniform-Type-Identifier: com.apple.mail-draft
-    //this could be added later if needed
-
-   
-    var mailObj = mail.getCachedMail(mailId);
-    return mail.toRFC(mailObj);
-}
-
-/**
- * opens the email
- */
-Email.prototype.openMail = function ()
-{
-    neon.openUrl(this.getMailtoUrl(), false);
-}
-
-/**
- * ask for a download of the email
- */
-Email.prototype.downloadEML = function()
-{
-    neon.download(util.encodeBase64String(this.getRFCmail(), null), (this.subject || translate.text("Email Template")) + ".eml");
-}
+import("system.translate");
+import("system.text");
+import("system.db");
+import("system.util");
+import("Communication_lib");
+import("DocumentTemplate_lib");
+import("system.neon");
+import("system.mail");
+
+function EmailUtils () {}
+
+/**
+ * creates a new E-Mail-Object and ask for a download of a eml where all fields are prefilled
+ * The eml can be open with a mailclient and sent via the mailclient. Each mailclient has a different behaviour:
+ * In Outlook the mail is automatically opened in draft-mode
+ * In Thunderbird the mail is opened in view mode and you've to manually "edit as new"
+ * 
+ * @param {String|Array} pToRecipients mailaddresses of the recipients, can either be a 1D-Array with several addresses or a string with one address
+ * @param {String} pSenderContactId contactId of the sender. the standard mailadress of the contact is used as sender-address
+ * @param {String} [pTemplateId] if a document-template shall be used, give the templateId here
+ * @param {String} [pRecipientContactId] contactId of the recipient, required to fill placeholders
+ */
+EmailUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId)
+{
+    var email = new Email(pToRecipients);
+    email.setSender(pSenderContactId);
+    if (pTemplateId)
+        email.setTemplate(pTemplateId, pRecipientContactId);
+
+    email.openMail();
+}
+
+/**
+ * opens a view where a new mail can be sent. In the view the use CAN select a DocumentTemplate if needed
+ * 
+ * @param {String} pToContactId contactId with contacts to filter the communication-addresses
+ */
+EmailUtils.openNewMail = function (pToContactId)
+{
+    var params = {
+        "ContactId_param" : pToContactId
+    };
+    neon.openContext("Email", "EmailEdit_view", null, neon.OPERATINGSTATE_NEW, params);
+}
+
+/**
+ * object for handling emails
+ * 
+ * @param {String|Array} [pToRecipients=[]] recipient email address or array of recipient email addresses
+ * @param {String} [pSender=null] email address of the sender
+ * @param {String} [pSubject=null] subject
+ * @param {String} [pBody=null] mail body
+ * @param {Array} [pCcRecipients=[]] array of recipient cc addresses
+ * @param {Array} [pBccRecipients=[]] array of recipient bcc addresses
+ * 
+ * @class
+ */
+function Email (pToRecipients, pSender, pSubject, pBody, pCcRecipients, pBccRecipients)
+{
+    if (pToRecipients && typeof(pToRecipients) == "string")
+        pToRecipients = [pToRecipients];
+    
+    this.sender = pSender;
+    this.subject = pSubject;
+    this.body = pBody;
+    this.toRecipients = pToRecipients || [];
+    this.ccRecipients = pCcRecipients || [];
+    this.bccRecipients = pBccRecipients || [];
+}
+
+/**
+ * loads a document template into the mail body
+ * 
+ * @param {String} pTemplateId the id of the template
+ * @param {String} pContactId the id of the template
+ * 
+ * @throws {Error} if the type of the template is invalid
+ */
+Email.prototype.setTemplate = function (pTemplateId, pContactId)
+{
+    var template = DocumentTemplate.loadTemplate(pTemplateId);
+    
+    //TODO: also set other properties if the template is a eml
+    if (template)
+    {
+        if (template.type != DocumentTemplate.types.EML && template.type != DocumentTemplate.types.HTML && template.type != DocumentTemplate.types.TXT)
+            throw new Error("Invalid document type for an email template");
+        
+        this.body = template.getReplacedContentByContactId(pContactId);
+    }
+}
+
+/**
+ * sets the sender of the mail
+ * 
+ * @param {String} pContactId the contactId of the sender
+ */
+Email.prototype.setSender = function (pContactId)
+{
+    this.sender = CommUtil.getStandardMail(pContactId);
+}
+
+/**
+ * generates a 'mailto:' URL from the email object
+ */
+Email.prototype.getMailtoUrl = function ()
+{
+    var url = [];
+    
+    if (this.toRecipients.length)
+        url.push("to=" + this.toRecipients.join());
+    
+    if (this.ccRecipients.length)
+        url.push("cc=" + this.ccRecipients.join());
+    
+    if (this.bccRecipients.length)
+        url.push("bcc=" + this.bccRecipients.join());
+    
+    if (this.subject)
+        url.push("subject=" + this.subject);
+    
+    if (this.body)
+        url.push("body=" + text.html2text(this.body));
+    
+    url = "mailto:?" + url.join("&");
+    
+    return encodeURI(url);
+}
+
+/**
+ * generates a eml-element from the email object
+ */
+Email.prototype.getRFCmail = function ()
+{
+    var ENCODING = "UTF-8";
+    var mailId = mail.newMail();
+
+    if (this.toRecipients.length)
+        mail.addRecipients(mailId, mail.RECIPIENT_TO, this.toRecipients);
+
+    if (this.ccRecipients.length)
+        mail.addRecipients(mailId, mail.RECIPIENT_CC, this.ccRecipients);
+
+    if (this.bccRecipients.length)
+        mail.addRecipients(mailId, mail.RECIPIENT_BCC, this.bccRecipients);
+
+    if (this.subject)
+        mail.setSubject(mailId, this.subject, ENCODING);
+
+    if (this.body)
+        mail.addText(mailId, this.body, "text/html", ENCODING, null);
+    else
+        mail.addText(mailId, "", "text/html", ENCODING, null);
+    
+    //"X-Unsent" is a very badly, non-standardised header to gently ask the mail client that the mail should open in a compose-mode
+    //this is mainly done for Microsoft Outlook for Windows. 
+    //Thunderbird has a dinosaur-request (it's from the year 2002) to also support this: https://bugzilla.mozilla.org/show_bug.cgi?id=166541 
+    mail.addHeader(mailId, "X-Unsent", "1");
+    
+    //accoding to this entry: https://stackoverflow.com/questions/11330628/os-x-mail-open-eml-files-in-compose-mode/33224913
+    //something similar exists for OS X Mail
+    //X-Uniform-Type-Identifier: com.apple.mail-draft
+    //this could be added later if needed
+
+   
+    var mailObj = mail.getCachedMail(mailId);
+    return mail.toRFC(mailObj);
+}
+
+/**
+ * opens the email
+ */
+Email.prototype.openMail = function ()
+{
+    neon.openUrl(this.getMailtoUrl(), false);
+}
+
+/**
+ * ask for a download of the email
+ */
+Email.prototype.downloadEML = function()
+{
+    neon.download(util.encodeBase64String(this.getRFCmail(), null), (this.subject || translate.text("Email Template")) + ".eml");
+}
     
\ No newline at end of file
diff --git a/process/KeywordData_lib/process.js b/process/KeywordData_lib/process.js
index c2f3f563fe..f64dd8e497 100644
--- a/process/KeywordData_lib/process.js
+++ b/process/KeywordData_lib/process.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("system.translate");
 import("system.db");
 import("DataCaching_lib");
@@ -93,7 +92,6 @@ KeywordData.getKeywordAttributeRelations = function (pKeywordContainer)
         }
         return res;
     });
-    logging.log(data.toSource());
     return data;
 }
 
diff --git a/process/Keyword_lib/process.js b/process/Keyword_lib/process.js
index a5ff004df2..a7d93bfb2f 100644
--- a/process/Keyword_lib/process.js
+++ b/process/Keyword_lib/process.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("KeywordRegistry_basic");
 import("KeywordData_lib");
 import("system.vars");
@@ -71,7 +70,6 @@ KeywordUtils.getAttributeRelationsByKey = function(pKeyId, pContainerName)
         return "";
     
     var data = KeywordData.getKeywordAttributeRelations(pContainerName);
-    logging.log("byKey " + data.toSource())
     if (data[pKeyId] == undefined)
         return {};
     else
@@ -92,10 +90,6 @@ KeywordUtils.getAttributeRelationsByKey = function(pKeyId, pContainerName)
 KeywordUtils.getAttributeRelation = function(pKeyId, pContainerName, pAttrName, pDefault)
 {
     var attributes = KeywordUtils.getAttributeRelationsByKey(pKeyId, pContainerName);
-    logging.log("getRel " + attributes.toSource());
-    logging.log("ggggg " + pAttrName);
-    logging.log("ggggg " + pKeyId);
-    logging.log("ggggg " + attributes[pAttrName]);
     if (attributes && attributes[pAttrName] != null)
     {
         return attributes[pAttrName];
diff --git a/process/Turnover_lib/process.js b/process/Turnover_lib/process.js
index cda5a8b009..4310d902ba 100644
--- a/process/Turnover_lib/process.js
+++ b/process/Turnover_lib/process.js
@@ -1,4 +1,3 @@
-import("system.logging");
 import("Sql_lib");
 import("system.translate");
 import("KeywordRegistry_basic");
@@ -87,13 +86,10 @@ TurnoverUtil.getForecastData = function (pMaxYear, pYearCount, pSalesprojectId)
 TurnoverUtil.getTurnoverAndForecastData = function (pMaxYear, pYearCount, pShowForecast, pShowTurnover, pSalesprojectId)
 {   
     var data = [];
-    logging.log(pShowTurnover);
-    logging.log(pShowForecast);
     if (pShowTurnover)
         data = data.concat(TurnoverUtil.getTurnoverData(pMaxYear, pYearCount, pSalesprojectId));
     
     if (pShowForecast)
         data = data.concat(TurnoverUtil.getForecastData(pMaxYear, pYearCount, pSalesprojectId));
-    logging.log(data.toSource());
     return data;
 }
\ No newline at end of file
-- 
GitLab