diff --git a/entity/AttributeRelation_entity/AttributeRelation_entity.aod b/entity/AttributeRelation_entity/AttributeRelation_entity.aod index 5d5c0da96881f87673fcd1d78115da11cc83ce40..69ec8245a8929cad2f06dc06a282f5488290e368 100644 --- a/entity/AttributeRelation_entity/AttributeRelation_entity.aod +++ b/entity/AttributeRelation_entity/AttributeRelation_entity.aod @@ -2,7 +2,11 @@ <entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.10" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.10"> <name>AttributeRelation_entity</name> <majorModelMode>DISTRIBUTED</majorModelMode> - <title>Attribute</title> + <titlePlural>Attributes</ti<grantDeleteProcess>%aditoprj%/entity/AttributeRelation_entity/grantDeleteProcess.js</grantDeleteProcess> + tlePlural> + <recordContainer>jdito</recordContainer> + <entityFields> + <entityProvider> <titlePlural>Attributes</titlePlural> <recordContainer>jdito</recordContainer> <entityFields> diff --git a/entity/AttributeRelation_entity/grantDeleteProcess.js b/entity/AttributeRelation_entity/grantDeleteProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..e407961574cd4f10e420c5b3615f955fa5db8731 --- /dev/null +++ b/entity/AttributeRelation_entity/grantDeleteProcess.js @@ -0,0 +1,8 @@ +import("Attribute_lib"); +import("system.result"); +import("system.vars"); + +var objectType = vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param"); +var rowId = vars.exists("$param.ObjectRowId_param") && vars.get("$param.ObjectRowId_param"); +if (vars.get("$param.GetTree_param") == "true" && rowId) + result.object(AttributeRelationUtils.countAttributeRelations(rowId, objectType)); \ No newline at end of file diff --git a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js index 249ba8308b6fa9ba2b0f912b649a0c43637f3d4a..54e00eb0a624331ee8d633d904a81cfcf5acdb1a 100644 --- a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js +++ b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js @@ -1,198 +1,198 @@ -import("system.translate"); -import("Util_lib"); -import("JditoFilter_lib"); -import("KeywordRegistry_basic"); -import("Keyword_lib"); -import("system.db"); -import("system.vars"); -import("system.result"); -import("Sql_lib"); -import("Attribute_lib"); - -var getGroups = vars.exists("$param.GetGroups_param") && vars.get("$param.GetGroups_param"); -var objectType = vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param"); -var parentType = vars.exists("$param.AttrParentType_param") && vars.get("$param.AttrParentType_param"); -var fetchUsages = true; -var translateName = false; - -var uidTableAlias = "UIDROW"; -var sqlSelect = "select UIDROW.AB_ATTRIBUTEID, UIDROW.ATTRIBUTE_PARENT_ID, UIDROW.ATTRIBUTE_ACTIVE, UIDROW.DROPDOWNDEFINITION, UIDROW.SORTING, UIDROW.ATTRIBUTE_TYPE, " - + KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.attributeType(), "UIDROW.ATTRIBUTE_TYPE") //3 - + ", '', UIDROW.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_PARENT_ID \n\ - from AB_ATTRIBUTE UIDROW \n\ - left join AB_ATTRIBUTE PARENT1 on UIDROW.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID \n\ - left join AB_ATTRIBUTE PARENT2 ON PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID \n\ - left join AB_ATTRIBUTE PARENT3 ON PARENT2.ATTRIBUTE_PARENT_ID = PARENT3.AB_ATTRIBUTEID"; - -/* always select the names of the next 3 parents so that less queries - are required later when buildung the full name */ - -var sqlOrder = " order by UIDROW.ATTRIBUTE_PARENT_ID asc, UIDROW.SORTING asc"; - -var condition = SqlCondition.begin(); - -if (vars.exists("$local.idvalues") && vars.get("$local.idvalues")) -{ - condition.and("UIDROW.AB_ATTRIBUTEID in ('" + vars.get("$local.idvalues").join("','") + "')"); -} -else if (getGroups) //if getGroups == true, it is the lookup for selecting the superordinate attribute -{ - //this condition filters out the own id and the children to prevent loops - - var isGroupCondition = SqlCondition.begin(); - for (let type in $AttributeTypes) - if ($AttributeTypes[type].isGroup) - { - isGroupCondition.orPrepare(["AB_ATTRIBUTE", "ATTRIBUTE_TYPE", uidTableAlias], $AttributeTypes[type]); - } - - condition.andSqlCondition(SqlCondition.begin() - .andSqlCondition(isGroupCondition) - .andPrepareVars(["AB_ATTRIBUTE", "AB_ATTRIBUTEID", uidTableAlias], "$param.AttrParentId_param", "# != ?") - .and("UIDROW.AB_ATTRIBUTEID not in ('" + AttributeUtil.getAllChildren(vars.getString("$param.AttrParentId_param")).join("','") + "')") - ); -} -else if (objectType) //if there's an objectType, it comes from the AttributeRelation entity (lookup for the attribute selection) -{ - var filteredAttributes = null; - - if (vars.exists("$param.FilteredAttributeIds_param") && vars.getString("$param.FilteredAttributeIds_param")) { - filteredAttributes = JSON.parse(vars.getString("$param.FilteredAttributeIds_param")); - } - - var attributeCount; - if (vars.exists("$param.AttributeCount_param") && vars.get("$param.AttributeCount_param")) - attributeCount = JSON.parse(vars.getString("$param.AttributeCount_param")); - var ids = AttributeUtil.getPossibleAttributes(objectType, false, filteredAttributes, attributeCount); - - if (ids.length > 0) - condition.and("UIDROW.AB_ATTRIBUTEID in ('" + ids.join("','") + "')"); - else // do not return anything, if parameter is there but an empty array - condition.and("1=2"); - - fetchUsages = false; - translateName = true; -} -else if (parentType) //condition for all subordinate attributes of an attribute (for the tree of subordinate attributes in an attribute) -{ - if (AttributeTypeUtil.isGroupType(parentType)) - { - var parentId = vars.exists("$param.AttrParentId_param") && vars.get("$param.AttrParentId_param"); - if (parentId) - condition.and("UIDROW.AB_ATTRIBUTEID in ('" + AttributeUtil.getAllChildren(vars.getString("$param.AttrParentId_param")).join("','") + "')"); - } - else - condition.and("1=2"); - - translateName = true; -} - -//when there are filters selected, add them to the conditon -if (vars.exists("$local.filter") && vars.get("$local.filter")) -{ - var filter = vars.get("$local.filter"); - if (filter.filter) - condition.andSqlCondition(JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE", uidTableAlias)); -} - -var usages; -if (fetchUsages) //this query is only necessary in Attribute, not in AttributeRelation -{ - var usagesSelect = "select AB_ATTRIBUTE_ID, OBJECT_TYPE from AB_ATTRIBUTEUSAGE \n\ - join AB_ATTRIBUTE UIDROW on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = UIDROW.AB_ATTRIBUTEID"; - var usageTbl = db.table(condition.buildSql(usagesSelect, "1=1")); - usages = {}; - for (let i = 0, l = usageTbl.length; i < l; i++) - { - let attrId = usageTbl[i][0]; - if (attrId in usages) - usages[attrId].push(usageTbl[i][1]); - else - usages[attrId] = [usageTbl[i][1]]; - } -} - -var attributes = db.table(condition.buildSql(sqlSelect, "1=1", sqlOrder)); - -var nameCache = {}; -result.object(_buildAttributeTable(attributes, usages, translateName)); - - -//sorts the records in a way that a tree can be built and adds values -function _buildAttributeTable (pAttributes, pUsages, pTranslate) -{ - var rows = {}; - var allIds = {}; - - //fills the allIds object, the object is used for checking if a parent exists in the array - for (let i = 0, l = pAttributes.length; i < l; i++) - allIds[pAttributes[i][0]] = true; - - var arrayIndex = 0; - - do { - var oldIndex = arrayIndex; - pAttributes.forEach(function (row) - { - //item will be added if the id is not already in the object and - //the parent is already added (or the parent is not in the array) - if (!(row[0] in this) && (row[1] in this || !allIds[row[1]])) - this[row[0]] = { - data : row, - index : arrayIndex++ - }; - }, rows); - } while (oldIndex != arrayIndex); //stops the loop when no new items were added so that recursive relations between attributes don't cause an infinite loop - - var displaySimpleName = vars.exists("$param.DisplaySimpleName_param") && vars.get("$param.DisplaySimpleName_param"); - var sortedArray = new Array(Object.keys(rows).length); - for (let i in rows) - { - let rowData = rows[i].data; - if (pUsages && rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) - { - rowData[7] = pUsages[i].map(function (usage) - { - return translate.text(usage); - }).join(", "); - } - var fullName; - if (displaySimpleName) - fullName = pTranslate - ? translate.text(rowData[8]) - : rowData[8]; - else - fullName = _getFullName(rowData[8], rowData[9], rowData[10], rowData[11], rowData[12], pTranslate); - rowData.splice(10, 3, fullName); - sortedArray[rows[i].index] = rowData; - } - - return sortedArray; - - - /** - * builds the full attribute name from the pre-loaded parent names and adds all parent names - * if required - */ - function _getFullName (pAttributeName, pParent1Name, pParent2Name, pParent3Name, pParent4Id) - { - var parent4FullName; - if (pParent4Id && pParent4Id in nameCache) - parent4FullName = nameCache[pParent4Id]; - else - { - parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id, false, pTranslate) : null; - nameCache[pParent4Id] = parent4FullName; - } - if (pTranslate) - { - pAttributeName = translate.text(pAttributeName); - pParent1Name = translate.text(pParent1Name); - pParent2Name = translate.text(pParent2Name); - pParent3Name = translate.text(pParent3Name); - } - pAttributeName = ArrayUtils.joinNonEmptyFields([parent4FullName, pParent3Name, pParent2Name, pParent1Name, pAttributeName], " / "); - - return pAttributeName; - } -} +import("system.translate"); +import("Util_lib"); +import("JditoFilter_lib"); +import("KeywordRegistry_basic"); +import("Keyword_lib"); +import("system.db"); +import("system.vars"); +import("system.result"); +import("Sql_lib"); +import("Attribute_lib"); + +var getGroups = vars.exists("$param.GetGroups_param") && vars.get("$param.GetGroups_param"); +var objectType = vars.exists("$param.ObjectType_param") && vars.get("$param.ObjectType_param"); +var parentType = vars.exists("$param.AttrParentType_param") && vars.get("$param.AttrParentType_param"); +var fetchUsages = true; +var translateName = false; + +var uidTableAlias = "UIDROW"; +var sqlSelect = "select UIDROW.AB_ATTRIBUTEID, UIDROW.ATTRIBUTE_PARENT_ID, UIDROW.ATTRIBUTE_ACTIVE, UIDROW.DROPDOWNDEFINITION, UIDROW.SORTING, UIDROW.ATTRIBUTE_TYPE, " + + KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.attributeType(), "UIDROW.ATTRIBUTE_TYPE") //3 + + ", '', UIDROW.ATTRIBUTE_NAME, PARENT1.ATTRIBUTE_NAME, PARENT2.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_NAME, PARENT3.ATTRIBUTE_PARENT_ID \n\ + from AB_ATTRIBUTE UIDROW \n\ + left join AB_ATTRIBUTE PARENT1 on UIDROW.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID \n\ + left join AB_ATTRIBUTE PARENT2 ON PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID \n\ + left join AB_ATTRIBUTE PARENT3 ON PARENT2.ATTRIBUTE_PARENT_ID = PARENT3.AB_ATTRIBUTEID"; + +/* always select the names of the next 3 parents so that less queries + are required later when buildung the full name */ + +var sqlOrder = " order by UIDROW.ATTRIBUTE_PARENT_ID asc, UIDROW.SORTING asc"; + +var condition = SqlCondition.begin(); + +if (vars.exists("$local.idvalues") && vars.get("$local.idvalues")) +{ + condition.and("UIDROW.AB_ATTRIBUTEID in ('" + vars.get("$local.idvalues").join("','") + "')"); +} +else if (getGroups) //if getGroups == true, it is the lookup for selecting the superordinate attribute +{ + //this condition filters out the own id and the children to prevent loops + + var isGroupCondition = SqlCondition.begin(); + for (let type in $AttributeTypes) + if ($AttributeTypes[type].isGroup) + { + isGroupCondition.orPrepare(["AB_ATTRIBUTE", "ATTRIBUTE_TYPE", uidTableAlias], $AttributeTypes[type]); + } + + condition.andSqlCondition(SqlCondition.begin() + .andSqlCondition(isGroupCondition) + .andPrepareVars(["AB_ATTRIBUTE", "AB_ATTRIBUTEID", uidTableAlias], "$param.AttrParentId_param", "# != ?") + .and("UIDROW.AB_ATTRIBUTEID not in ('" + AttributeUtil.getAllChildren(vars.getString("$param.AttrParentId_param")).join("','") + "')") + ); +} +else if (objectType) //if there's an objectType, it comes from the AttributeRelation entity (lookup for the attribute selection) +{ + var filteredAttributes = null; + + if (vars.exists("$param.FilteredAttributeIds_param") && vars.getString("$param.FilteredAttributeIds_param")) { + filteredAttributes = JSON.parse(vars.getString("$param.FilteredAttributeIds_param")); + } + + var attributeCount; + if (vars.exists("$param.AttributeCount_param") && vars.get("$param.AttributeCount_param")) + attributeCount = JSON.parse(vars.getString("$param.AttributeCount_param")); + var ids = AttributeUtil.getPossibleAttributes(objectType, false, filteredAttributes, attributeCount); + + if (ids.length > 0) + condition.and("UIDROW.AB_ATTRIBUTEID in ('" + ids.join("','") + "')"); + else // do not return anything, if parameter is there but an empty array + condition.and("1=2"); + + fetchUsages = false; + translateName = true; +} +else if (parentType) //condition for all subordinate attributes of an attribute (for the tree of subordinate attributes in an attribute) +{ + if (AttributeTypeUtil.isGroupType(parentType)) + { + var parentId = vars.exists("$param.AttrParentId_param") && vars.get("$param.AttrParentId_param"); + if (parentId) + condition.and("UIDROW.AB_ATTRIBUTEID in ('" + AttributeUtil.getAllChildren(vars.getString("$param.AttrParentId_param")).join("','") + "')"); + } + else + condition.and("1=2"); + + translateName = true; +} + +//when there are filters selected, add them to the conditon +if (vars.exists("$local.filter") && vars.get("$local.filter")) +{ + var filter = vars.get("$local.filter"); + if (filter.filter) + condition.andSqlCondition(JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE", uidTableAlias)); +} + +var usages; +if (fetchUsages) //this query is only necessary in Attribute, not in AttributeRelation +{ + var usagesSelect = "select AB_ATTRIBUTE_ID, OBJECT_TYPE from AB_ATTRIBUTEUSAGE \n\ + join AB_ATTRIBUTE UIDROW on AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = UIDROW.AB_ATTRIBUTEID"; + var usageTbl = db.table(condition.buildSql(usagesSelect, "1=1")); + usages = {}; + for (let i = 0, l = usageTbl.length; i < l; i++) + { + let attrId = usageTbl[i][0]; + if (attrId in usages) + usages[attrId].push(usageTbl[i][1]); + else + usages[attrId] = [usageTbl[i][1]]; + } +} + +var attributes = db.table(condition.buildSql(sqlSelect, "1=1", sqlOrder)); + +var nameCache = {}; +result.object(_buildAttributeTable(attributes, usages, translateName)); + + +//sorts the records in a way that a tree can be built and adds values +function _buildAttributeTable (pAttributes, pUsages, pTranslate) +{ + var rows = {}; + var allIds = {}; + + //fills the allIds object, the object is used for checking if a parent exists in the array + for (let i = 0, l = pAttributes.length; i < l; i++) + allIds[pAttributes[i][0]] = true; + + var arrayIndex = 0; + + do { + var oldIndex = arrayIndex; + pAttributes.forEach(function (row) + { + //item will be added if the id is not already in the object and + //the parent is already added (or the parent is not in the array) + if (!(row[0] in this) && (row[1] in this || !allIds[row[1]])) + this[row[0]] = { + data : row, + index : arrayIndex++ + }; + }, rows); + } while (oldIndex != arrayIndex); //stops the loop when no new items were added so that recursive relations between attributes don't cause an infinite loop + + var displaySimpleName = vars.exists("$param.DisplaySimpleName_param") && vars.get("$param.DisplaySimpleName_param"); + var sortedArray = new Array(Object.keys(rows).length); + for (let i in rows) + { + let rowData = rows[i].data; + if (pUsages && rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) + { + rowData[7] = pUsages[i].map(function (usage) + { + return translate.text(usage); + }).join(", "); + } + var fullName; + if (displaySimpleName) + fullName = pTranslate + ? translate.text(rowData[8]) + : rowData[8]; + else + fullName = _getFullName(rowData[8], rowData[9], rowData[10], rowData[11], rowData[12], pTranslate); + rowData.splice(10, 3, fullName); + sortedArray[rows[i].index] = rowData; + } + + return sortedArray; + + + /** + * builds the full attribute name from the pre-loaded parent names and adds all parent names + * if required + */ + function _getFullName (pAttributeName, pParent1Name, pParent2Name, pParent3Name, pParent4Id) + { + var parent4FullName; + if (pParent4Id && pParent4Id in nameCache) + parent4FullName = nameCache[pParent4Id]; + else + { + parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id, false, pTranslate) : null; + nameCache[pParent4Id] = parent4FullName; + } + if (pTranslate) + { + pAttributeName = translate.text(pAttributeName); + pParent1Name = translate.text(pParent1Name); + pParent2Name = translate.text(pParent2Name); + pParent3Name = translate.text(pParent3Name); + } + pAttributeName = ArrayUtils.joinNonEmptyFields([parent4FullName, pParent3Name, pParent2Name, pParent1Name, pAttributeName], " / "); + + return pAttributeName; + } +} diff --git a/entity/BulkMail_entity/BulkMail_entity.aod b/entity/BulkMail_entity/BulkMail_entity.aod index 14908fdd93218c7bbf3d723cd28d32e128402702..39f2deaae84dbb1f3a921d8dec0e5bf591fb8109 100644 --- a/entity/BulkMail_entity/BulkMail_entity.aod +++ b/entity/BulkMail_entity/BulkMail_entity.aod @@ -84,6 +84,7 @@ <name>SENDER</name> <title>Sender address</title> <mandatory v="true" /> + <onValidation>%aditoprj%/entity/BulkMail_entity/entityfields/sender/onValidation.js</onValidation> </entityField> <entityActionField> <name>sendMail</name> @@ -95,11 +96,13 @@ <entityField> <name>ICON</name> <contentType>IMAGE</contentType> + <searchable v="false" /> <valueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/icon/valueProcess.js</valueProcess> </entityField> <entityField> <name>preview</name> <contentType>HTML</contentType> + <searchable v="false" /> <state>READONLY</state> <displayValueProcess>%aditoprj%/entity/BulkMail_entity/entityfields/preview/displayValueProcess.js</displayValueProcess> </entityField> @@ -120,6 +123,7 @@ <entityField> <name>BINDATA</name> <contentType>FILE</contentType> + <searchable v="false" /> <stateProcess>%aditoprj%/entity/BulkMail_entity/entityfields/bindata/stateProcess.js</stateProcess> </entityField> <entityFieldGroup> @@ -143,6 +147,7 @@ <alias>Data_alias</alias> <fromClauseProcess>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess> <onDBInsert>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBInsert.js</onDBInsert> + <onDBUpdate>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js</onDBUpdate> <onDBDelete>%aditoprj%/entity/BulkMail_entity/recordcontainers/db/onDBDelete.js</onDBDelete> <linkInformation> <linkInformation> diff --git a/entity/BulkMail_entity/entityfields/sender/onValidation.js b/entity/BulkMail_entity/entityfields/sender/onValidation.js new file mode 100644 index 0000000000000000000000000000000000000000..a07daba57f94e4328ff3907479205d93e38a93db --- /dev/null +++ b/entity/BulkMail_entity/entityfields/sender/onValidation.js @@ -0,0 +1,11 @@ +import("system.result"); +import("Communication_lib"); +import("Entity_lib"); + +var message; +var sender = ProcessHandlingUtils.getOnValidationValue(); +var fn = CommValidationUtil.makeValidationFn("EMAIL"); +if (fn !== null) + message = fn.call(null, sender); +if (message) + result.string(message); \ No newline at end of file diff --git a/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js b/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js new file mode 100644 index 0000000000000000000000000000000000000000..4b8b28c49a9c31968b3cc5b34883d746bdc50193 --- /dev/null +++ b/entity/BulkMail_entity/recordcontainers/db/onDBUpdate.js @@ -0,0 +1,28 @@ +import("Sql_lib"); +import("system.result"); +import("system.vars"); +import("system.db"); +import("system.util"); +import("Document_lib"); + +//TODO - Function + +var upload = vars.get("$field.BINDATA"); +var bindataUpload = DocumentUtil.getBindataFromUpload(upload); +var filename = ""; +var bindata = ""; + +if(bindataUpload != "") +{ + filename = DocumentUtil.getFilenameFromUpload(upload); + bindata = bindataUpload; +} + +if(bindata != "" && filename != "") +{ + let sysAlias = "_____SYSTEMALIAS"; + var binaryId = db.cell(SqlCondition.begin(sysAlias) + .andPrepareVars("ASYS_BINARIES.ROW_ID", "$field.BULKMAILID") + .buildSql("select ID from ASYS_BINARIES", "1=2"), sysAlias); + db.updateBinary(binaryId, "", bindata, filename, "", "", sysAlias); +} \ No newline at end of file diff --git a/entity/SerialLetter_entity/entityfields/downloadletter/onActionProcess.js b/entity/SerialLetter_entity/entityfields/downloadletter/onActionProcess.js index 0b4d4abfc7f03b415d5494e33188d75a1a5384d4..bdf5e858524fedc80fa17146b801349638643407 100644 --- a/entity/SerialLetter_entity/entityfields/downloadletter/onActionProcess.js +++ b/entity/SerialLetter_entity/entityfields/downloadletter/onActionProcess.js @@ -1,17 +1,6 @@ -import("KeywordRegistry_basic"); -import("Contact_lib"); -import("Sql_lib"); -import("system.db"); -import("system.neon"); -import("system.vars"); -import("DocumentTemplate_lib"); - -var template = DocumentTemplate.loadTemplate(vars.get("$field.DOCUMENTTEMPLATE_ID")); -var contactIds = db.table(SqlCondition.begin() - .andPrepareVars("LETTERRECIPIENT.SERIALLETTER_ID", "$field.SERIALLETTERID") - .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$letter(), true)) - .buildSql("select CONTACT_ID from LETTERRECIPIENT join CONTACT on LETTERRECIPIENT.CONTACT_ID = CONTACT.CONTACTID", "1=2") -); -var document = template.getSerialLetterByContactIds(contactIds); -if (document) - neon.download(document, template.filename); \ No newline at end of file +import("DocumentTemplate_lib"); +import("system.neon"); +import("Bulkmail_lib"); +import("system.vars"); + +var document = SerialLetterUtils.buildSerialLetter(vars.get("$field.SERIALLETTERID")); \ No newline at end of file diff --git a/neonNotificationType/BulkMailSent/BulkMailSent.aod b/neonNotificationType/BulkMailSent/BulkMailSent.aod index 63b6ad9c3e2da370cdd6b33a807b1852aece2ed8..77a69c9942425bf9543abff0c2a3d63df5db0fcc 100644 --- a/neonNotificationType/BulkMailSent/BulkMailSent.aod +++ b/neonNotificationType/BulkMailSent/BulkMailSent.aod @@ -1,7 +1,7 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonNotificationType xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.0" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonNotificationType/1.1.0"> - <name>BulkMailSent</name> - <title>Bulk mail sent</title> - <majorModelMode>DISTRIBUTED</majorModelMode> - <icon>VAADIN:ENVELOPES</icon> -</neonNotificationType> +<?xml version="1.0" encoding="UTF-8"?> +<neonNotificationType xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.0" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonNotificationType/1.1.0"> + <name>BulkMailSent</name> + <title>Bulk mail sent</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <icon>VAADIN:ENVELOPES</icon> +</neonNotificationType> diff --git a/neonNotificationType/DownloadReady/DownloadReady.aod b/neonNotificationType/DownloadReady/DownloadReady.aod new file mode 100644 index 0000000000000000000000000000000000000000..b8b50011b3a3fb6778905aecff612ab1a8446d40 --- /dev/null +++ b/neonNotificationType/DownloadReady/DownloadReady.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<neonNotificationType xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.0" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonNotificationType/1.1.0"> + <name>DownloadReady</name> + <title>Download ready</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <icon>VAADIN:DOWNLOAD_ALT</icon> + <archivable v="false" /> + <onResultOpen>%aditoprj%/neonNotificationType/DownloadReady/onResultOpen.js</onResultOpen> +</neonNotificationType> diff --git a/neonNotificationType/DownloadReady/onResultOpen.js b/neonNotificationType/DownloadReady/onResultOpen.js new file mode 100644 index 0000000000000000000000000000000000000000..05f211c5511cd30430f3c0141712fe4a7a903367 --- /dev/null +++ b/neonNotificationType/DownloadReady/onResultOpen.js @@ -0,0 +1,10 @@ +import("system.logging"); +import("system.vars"); + +var varses = ["$"]; +var existses = {}; +varses.forEach(function (v) { + this[v] = vars.exists(v); +}, existses); + +logging.log(JSON.stringify(existses, null, "\t")) \ No newline at end of file diff --git a/neonView/BulkMailAddRecipientsEdit_view/BulkMailAddRecipientsEdit_view.aod b/neonView/BulkMailAddRecipientsEdit_view/BulkMailAddRecipientsEdit_view.aod index 4407b8799f8672d03560f4ae529b57d6819d1385..e8b7469a6fc7e2208409757a38a938ce83efbe2d 100644 --- a/neonView/BulkMailAddRecipientsEdit_view/BulkMailAddRecipientsEdit_view.aod +++ b/neonView/BulkMailAddRecipientsEdit_view/BulkMailAddRecipientsEdit_view.aod @@ -1,35 +1,35 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.1"> - <name>BulkMailAddRecipientsEdit_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <isSmall v="true" /> - <layout> - <noneLayout> - <name>layout</name> - </noneLayout> - </layout> - <children> - <genericViewTemplate> - <name>Generic</name> - <editMode v="true" /> - <entityField>#ENTITY</entityField> - <fields> - <entityFieldLink> - <name>96932894-43f2-471f-b511-3b38bd5f93cb</name> - <entityField>BULKMAIL_ID</entityField> - </entityFieldLink> - </fields> - </genericViewTemplate> - <genericViewTemplate> - <name>Message</name> - <hideLabels v="true" /> - <entityField>#ENTITY</entityField> - <fields> - <entityFieldLink> - <name>e29ba637-3638-4c72-bdb7-65034636a882</name> - <entityField>recipientCountMessage</entityField> - </entityFieldLink> - </fields> - </genericViewTemplate> - </children> -</neonView> +<?xml version="1.0" encoding="UTF-8"?> +<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.1"> + <name>BulkMailAddRecipientsEdit_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <isSmall v="true" /> + <layout> + <noneLayout> + <name>layout</name> + </noneLayout> + </layout> + <children> + <genericViewTemplate> + <name>Generic</name> + <editMode v="true" /> + <entityField>#ENTITY</entityField> + <fields> + <entityFieldLink> + <name>96932894-43f2-471f-b511-3b38bd5f93cb</name> + <entityField>BULKMAIL_ID</entityField> + </entityFieldLink> + </fields> + </genericViewTemplate> + <genericViewTemplate> + <name>Message</name> + <hideLabels v="true" /> + <entityField>#ENTITY</entityField> + <fields> + <entityFieldLink> + <name>e29ba637-3638-4c72-bdb7-65034636a882</name> + <entityField>recipientCountMessage</entityField> + </entityFieldLink> + </fields> + </genericViewTemplate> + </children> +</neonView> diff --git a/neonView/BulkMailEdit_view/BulkMailEdit_view.aod b/neonView/BulkMailEdit_view/BulkMailEdit_view.aod index fa4ece93b65ff37c3fb38c7105bbe9d5db336d36..0f2c9009d08069b40cd8082687e39250ec8c88bc 100644 --- a/neonView/BulkMailEdit_view/BulkMailEdit_view.aod +++ b/neonView/BulkMailEdit_view/BulkMailEdit_view.aod @@ -1,43 +1,43 @@ -<?xml version="1.0" encoding="UTF-8"?> -<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.1"> - <name>BulkMailEdit_view</name> - <majorModelMode>DISTRIBUTED</majorModelMode> - <layout> - <boxLayout> - <name>layout</name> - </boxLayout> - </layout> - <children> - <genericViewTemplate> - <name>BulkMail</name> - <editMode v="true" /> - <entityField>#ENTITY</entityField> - <fields> - <entityFieldLink> - <name>b68c65de-4ecd-4a23-9242-f85e7b708b1e</name> - <entityField>DOCUMENTTEMPLATE_ID</entityField> - </entityFieldLink> - <entityFieldLink> - <name>f040d032-823c-4199-8314-01d784fdc167</name> - <entityField>BINDATA</entityField> - </entityFieldLink> - <entityFieldLink> - <name>e363bda2-d8bf-456e-bcae-d1870408022a</name> - <entityField>NAME</entityField> - </entityFieldLink> - <entityFieldLink> - <name>06f08869-5a81-41cb-8c7e-51be6a7041a7</name> - <entityField>DESCRIPTION</entityField> - </entityFieldLink> - <entityFieldLink> - <name>c73d0fb7-b740-48ac-8f3e-fd4199f169da</name> - <entityField>SUBJECT</entityField> - </entityFieldLink> - <entityFieldLink> - <name>e4ec09c2-3815-4a3b-bce8-c12d5b919b04</name> - <entityField>SENDER</entityField> - </entityFieldLink> - </fields> - </genericViewTemplate> - </children> -</neonView> +<?xml version="1.0" encoding="UTF-8"?> +<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.1"> + <name>BulkMailEdit_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <layout> + <boxLayout> + <name>layout</name> + </boxLayout> + </layout> + <children> + <genericViewTemplate> + <name>BulkMail</name> + <editMode v="true" /> + <entityField>#ENTITY</entityField> + <fields> + <entityFieldLink> + <name>b68c65de-4ecd-4a23-9242-f85e7b708b1e</name> + <entityField>DOCUMENTTEMPLATE_ID</entityField> + </entityFieldLink> + <entityFieldLink> + <name>f040d032-823c-4199-8314-01d784fdc167</name> + <entityField>BINDATA</entityField> + </entityFieldLink> + <entityFieldLink> + <name>e363bda2-d8bf-456e-bcae-d1870408022a</name> + <entityField>NAME</entityField> + </entityFieldLink> + <entityFieldLink> + <name>06f08869-5a81-41cb-8c7e-51be6a7041a7</name> + <entityField>DESCRIPTION</entityField> + </entityFieldLink> + <entityFieldLink> + <name>c73d0fb7-b740-48ac-8f3e-fd4199f169da</name> + <entityField>SUBJECT</entityField> + </entityFieldLink> + <entityFieldLink> + <name>e4ec09c2-3815-4a3b-bce8-c12d5b919b04</name> + <entityField>SENDER</entityField> + </entityFieldLink> + </fields> + </genericViewTemplate> + </children> +</neonView> diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js index 48a86f98145d53a3fcc52d5fd774554ad5dc9c6a..aa9a59ff63531e8ea617a175ed1493335ae5b8df 100644 --- a/process/Bulkmail_lib/process.js +++ b/process/Bulkmail_lib/process.js @@ -1,183 +1,189 @@ -import("system.util"); -import("Contact_lib"); -import("system.datetime"); -import("system.neon"); -import("Employee_lib"); -import("system.vars"); -import("KeywordRegistry_basic"); -import("Sql_lib"); -import("system.db"); -import("DocumentTemplate_lib"); -import("Email_lib"); -import("system.process"); - -/** - * functions for bulk mails - */ -function BulkMailUtils () {} - -/** - * Executes a process to send bulk mails on the server and creates a notification when finished. - * - * @param {String} pBulkMailId id of the bulk mail - * @param {String} [pUser=currentUser] User that will get the notification, if null (not undefined!), no notification - * will be created. - */ -BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pUser) -{ - if (pUser === undefined) - pUser = EmployeeUtils.getCurrentUserId(); - process.execute("sendBulkMail_serverProcess", - { - bulkMailId : pBulkMailId, - user : pUser || "" - } - ); -} - -/** - * Sends a bulk mail. You should only call this function on the server because it - * can take some time to execute, use BulkMailUtils.sendBulkMailOnServer instead. - * - * @param {String} pBulkMailId id of the bulk mail - * - * @return {Object} count of sucessful and failed mails - */ -BulkMailUtils.sendBulkMail = function (pBulkMailId) -{ - //TODO: Mailbridge, Werbesperre beachten - - var [templateId, subject, sender] = db.array(db.ROW, SqlCondition.begin() - .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId) - .buildSql("select DOCUMENTTEMPLATE_ID, SUBJECT, SENDER from BULKMAIL", "1=2") - ); - var template; - if (templateId) - template = DocumentTemplate.loadTemplate(templateId); - else - template = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL"); - var emailSender; - - var recipientData = db.table(SqlCondition.begin() - .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) - .andPrepare("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), "# != ?") - .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)) - .buildSql("select BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, '' from BULKMAILRECIPIENT \n\ - join CONTACT on BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID", "1=2") - ); - var contactIds = recipientData.map(function (e) {return e[1];}); - var successIds = []; - var failedIds = []; - var sentDate = vars.get("$sys.date"); - var mails = template.getReplacedEmailsByContactIds(contactIds); - - var subjectTemplate = new DocumentTemplate(subject, DocumentTemplate.types.PLAIN); - var subjects = subjectTemplate.getReplacedContentByContactIds(contactIds); - - for (let i = 0, l = recipientData.length; i < l; i++) - { - let isSuccess = false; - let contactId = recipientData[i][1]; - let email = mails[contactId]; - if (email !== undefined && recipientData[i][2]) - { - email.toRecipients = [recipientData[i][2]]; //TODO: email address missing - email.sender = emailSender; - email.subject = subjects[contactId]; - - isSuccess = email.send(); - } - if (isSuccess) - successIds.push(recipientData[i][0]); //set the recipient status to 'sent' - else - failedIds.push(recipientData[i][0]); //set the recipient status to 'failed' - } - db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate], - SqlCondition.begin() - .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds) - .build("1=2") - ); - db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate], - SqlCondition.begin() - .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds) - .build("1=2") - ); - - db.updateData("BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()], - SqlCondition.equals("BULKMAIL.BULKMAILID", pBulkMailId, "1=2")); - - return { - sucessful : successIds.length, - failed : failedIds.length - }; -} - -BulkMailUtils.openAddRecipientView = function (pContactIds) -{ - var params = { - "ContactIds_param" : pContactIds - }; - neon.openContext("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); -} - -BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId) -{ - var recipientIds = db.array(db.COLUMN, SqlBuilder.begin() - .select("BULKMAILRECIPIENTID") - .from("BULKMAILRECIPIENT") - .join("CONTACT", SqlCondition.begin() - .and("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") - .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail()))) - .where(SqlCondition.begin() - .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)) - .build()); - - if (recipientIds.length) - { - db.deleteData("BULKMAILRECIPIENT", SqlCondition.begin() - .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds) - .build("1=2")); - } -} - -BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds) -{ - var columns = [ - "BULKMAILRECIPIENTID", - "BULKMAIL_ID", - "CONTACT_ID", - "STATUS" - ]; - var inserts = []; - for (let i = 0, l = pContactIds.length; i < l; i++) - { - inserts.push(["BULKMAILRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i], $KeywordRegistry.bulkMailRecipientStatus$pending()]]); - } - db.inserts(inserts); -} - -function SerialLetterUtils () {} - -SerialLetterUtils.addRecipients = function (pBulkMailId, pContactIds) -{ - var columns = [ - "LETTERRECIPIENTID", - "SERIALLETTER_ID", - "CONTACT_ID" - ]; - var inserts = []; - for (let i = 0, l = pContactIds.length; i < l; i++) - { - inserts.push(["LETTERRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i]]]); - } - db.inserts(inserts); -} - - -SerialLetterUtils.openAddRecipientView = function (pContactIds) -{ - var params = { - "ContactIds_param" : pContactIds - }; - neon.openContext("SerialLetterAddRecipients", "SerialLetterAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); +import("system.util"); +import("Contact_lib"); +import("system.datetime"); +import("system.neon"); +import("Employee_lib"); +import("system.vars"); +import("KeywordRegistry_basic"); +import("Sql_lib"); +import("system.db"); +import("DocumentTemplate_lib"); +import("Email_lib"); +import("system.process"); + +/** + * functions for bulk mails + */ +function BulkMailUtils () {} + +/** + * Executes a process to send bulk mails on the server and creates a notification when finished. + * + * @param {String} pBulkMailId id of the bulk mail + * @param {String} [pUser=currentUser] User that will get the notification, if null (not undefined!), no notification + * will be created. + */ +BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pUser) +{ + if (pUser === undefined) + pUser = EmployeeUtils.getCurrentUserId(); + process.execute("sendBulkMail_serverProcess", + { + bulkMailId : pBulkMailId, + user : pUser || "" + } + ); +} + +/** + * Sends a bulk mail. You should only call this function on the server because it + * can take some time to execute, use BulkMailUtils.sendBulkMailOnServer instead. + * + * @param {String} pBulkMailId id of the bulk mail + * + * @return {Object} count of sucessful and failed mails + */ +BulkMailUtils.sendBulkMail = function (pBulkMailId) +{ + //TODO: Mailbridge, Werbesperre beachten + + var [templateId, subject, emailSender] = db.array(db.ROW, SqlCondition.begin() + .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId) + .buildSql("select DOCUMENTTEMPLATE_ID, SUBJECT, SENDER from BULKMAIL", "1=2") + ); + var template; + if (templateId) + template = DocumentTemplate.loadTemplate(templateId); + else + template = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL"); + + var recipientData = db.table(SqlCondition.begin() + .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) + .andPrepare("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), "# != ?") + .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)) + .buildSql("select BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, '' from BULKMAILRECIPIENT \n\ + join CONTACT on BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID", "1=2") + ); + var contactIds = recipientData.map(function (e) {return e[1];}); + var successIds = []; + var failedIds = []; + var sentDate = vars.get("$sys.date"); + var mails = template.getReplacedEmailsByContactIds(contactIds); + + var subjectTemplate = new DocumentTemplate(subject, DocumentTemplate.types.PLAIN); + var subjects = subjectTemplate.getReplacedContentByContactIds(contactIds); + + for (let i = 0, l = recipientData.length; i < l; i++) + { + let isSuccess = false; + let contactId = recipientData[i][1]; + let email = mails[contactId]; + if (email !== undefined && recipientData[i][2]) + { + email.toRecipients = [recipientData[i][2]]; //TODO: email address missing + email.sender = emailSender; + email.subject = subjects[contactId]; + + isSuccess = email.send(); + } + if (isSuccess) + successIds.push(recipientData[i][0]); //set the recipient status to 'sent' + else + failedIds.push(recipientData[i][0]); //set the recipient status to 'failed' + } + db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds) + .build("1=2") + ); + db.updateData("BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds) + .build("1=2") + ); + + db.updateData("BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()], + SqlCondition.equals("BULKMAIL.BULKMAILID", pBulkMailId, "1=2")); + + return { + sucessful : successIds.length, + failed : failedIds.length + }; +} + +BulkMailUtils.openAddRecipientView = function (pContactIds) +{ + var params = { + "ContactIds_param" : pContactIds + }; + neon.openContext("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); +} + +BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId) +{ + var recipientIds = db.array(db.COLUMN, SqlBuilder.begin() + .select("BULKMAILRECIPIENTID") + .from("BULKMAILRECIPIENT") + .join("CONTACT", SqlCondition.begin() + .and("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") + .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail()))) + .where(SqlCondition.begin() + .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)) + .build()); + + if (recipientIds.length) + { + db.deleteData("BULKMAILRECIPIENT", SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds) + .build("1=2")); + } +} + +BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds) +{ + var columns = [ + "BULKMAILRECIPIENTID", + "BULKMAIL_ID", + "CONTACT_ID", + "STATUS" + ]; + var inserts = []; + for (let i = 0, l = pContactIds.length; i < l; i++) + { + inserts.push(["BULKMAILRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i], $KeywordRegistry.bulkMailRecipientStatus$pending()]]); + } + db.inserts(inserts); +} + +function SerialLetterUtils () {} + +SerialLetterUtils.addRecipients = function (pBulkMailId, pContactIds) +{ + var columns = [ + "LETTERRECIPIENTID", + "SERIALLETTER_ID", + "CONTACT_ID" + ]; + var inserts = []; + for (let i = 0, l = pContactIds.length; i < l; i++) + { + inserts.push(["LETTERRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContactIds[i]]]); + } + db.inserts(inserts); +} + + +SerialLetterUtils.openAddRecipientView = function (pContactIds) +{ + var params = { + "ContactIds_param" : pContactIds + }; + neon.openContext("SerialLetterAddRecipients", "SerialLetterAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); +} + +SerialLetterUtils.buildSerialLetter = function (pSerialLetterId) +{ + process.executeAsync("buildSerialLetter_serverProcess", { + "serialLetterId" : pSerialLetterId + }, false, "_____USER_bcdfb521-c7d0-4ef1-8916-78e7d3232046", process.THREADPRIORITY_VERYHIGH); } \ No newline at end of file diff --git a/process/DocumentTemplate_lib/process.js b/process/DocumentTemplate_lib/process.js index 6b72a2999d6adb049e89764d555451f40afab0db..4198d8c2bdb353edb4c11d907fc6ea5f28dafa50 100644 --- a/process/DocumentTemplate_lib/process.js +++ b/process/DocumentTemplate_lib/process.js @@ -1,435 +1,436 @@ -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"); -import("Placeholder_lib"); -import("Email_lib"); - -/** - * Object for working with document templates, holds the content and type of the template. - * Provides functions to replace placeholders in the content. - * - * @class - */ -function DocumentTemplate (pTemplateContent, pType, pFilename) -{ - this.content = pTemplateContent; - this.type = pType; - this.filename = pFilename; -} - -DocumentTemplate.prototype.toString = function () -{ - if (this.type == DocumentTemplate.types.PLAIN) - return this.content; - return text.parseDocument(this.content); -} - -/** - * The types a DocumentTemplate can have. Depending on the type, - * the correct method for replacing the placeholders can be chosen - * - * @enum {String} - */ -DocumentTemplate.types = { - TXT : "txt", - HTML : "html", - EML : "eml", - ODT : "odt", - DOCX : "docx", - PLAIN : "plain" //for simple strings -}; - -/** - * Loads the content of a document template and creates a new DocumentTemplate object with that. - * - * @param {String} pAssignmentRowId id of the assignment (in most cases the document template id) - * @param {String} [pAssignmentTable="DOCUMENTTEMPLATE"] the LOB assignment table - * - * @return {DocumentTemplate} template object - * - * @throws {Error} if the type can't be used - */ -DocumentTemplate.loadTemplate = function (pAssignmentRowId, pAssignmentTable) -{ - var alias = "_____SYSTEMALIAS"; - if (!pAssignmentTable) - pAssignmentTable = "DOCUMENTTEMPLATE"; - var templateDocument = db.getBinaryMetadata(pAssignmentTable, "DOCUMENT", pAssignmentRowId, false, alias, null); - if (!templateDocument[0]) - return new DocumentTemplate(); - var binaryId = templateDocument[0][db.BINARY_ID]; - var filename = templateDocument[0][db.BINARY_FILENAME]; - var mimetype = templateDocument[0][db.BINARY_MIMETYPE]; - var typeMap = { - "text/plain" : DocumentTemplate.types.TXT, - "text/html" : DocumentTemplate.types.HTML, - "message/rfc822" : DocumentTemplate.types.EML, - "application/vnd.oasis.opendocument.text" : DocumentTemplate.types.ODT, - "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : DocumentTemplate.types.DOCX - }; - var type = typeMap[mimetype]; - if (type === undefined) - throw new Error("Invalid mime type for document template"); - - return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename); -} - -/** - * Replace function that works with strings instead of regular expressions - * so that control characters (for example '{', '}') don't have to be escaped. - * - * @private - */ -DocumentTemplate._replaceText = function (pText, pReplacements) -{ - for (let placeholder in pReplacements) - pText = pText.replace(placeholder, pReplacements[placeholder], "ig"); - return pText; -} - -/** - * returns the 'simpleName' of all placeholders that are used in the template - * - * @private - */ -DocumentTemplate.prototype._getRequiredPlaceholders = function () -{ - var allPlaceholders = PlaceholderUtils.getPlaceholders(); - var plainText = this.toString(); - var usedPlaceholders = []; - for (let i = 0, l = allPlaceholders.length; i < l; i++) - { - if (plainText.indexOf(allPlaceholders[i]) !== -1) - usedPlaceholders.push(allPlaceholders[i]); - } - return usedPlaceholders; -} - -/** - * 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); - case DocumentTemplate.types.PLAIN: - return DocumentTemplate._replaceText(this.content, pReplacements); - default: - return null; - } -} - -/** - * replaces the placeholders with data from one contact and returns the result - */ -DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) -{ - var replacements = this._getReplacementsByContactIds([pContactId]); - return this.getReplacedContent(replacements[pContactId]); -} - -/** - * replaces the placeholders with data from the contacts and returns the result - * - * @param {Array} pContactIds contact ids - * - * @return {Object} replaced content for every contactId - */ -DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds) -{ - var replacements = this._getReplacementsByContactIds(pContactIds); - var contents = {}; - for (let contactId in replacements) - { - contents[contactId] = this.getReplacedContent(replacements[contactId]); - } - return contents; -} - -/** - * Replaces the placeholders with data from the contacts and returns a serial letter, works - * only for ODT - * - * @param {Array} pContactIds contact ids - * - * @return {Object} the content of the replaced ODT - */ -DocumentTemplate.prototype.getSerialLetterByContactIds = function (pContactIds) -{ - var replacements = this._getReplacementsByContactIds(pContactIds); - if (this.type == DocumentTemplate.types.ODT) - { - let replaceArray = []; - for (let i = 0, l = pContactIds.length; i < l; i++) - replaceArray.push(replacements[pContactIds[i]]); - return this._getReplacedODT(replaceArray); - } - return null; -} - -/** - * Replaces the placeholders with data from the contacts and returns the resulting Emails. - * - * @param {Array} pContactIds contact ids - * - * @return {Object} Object containing the contact ids as keys and the corresponding Email - * objects as values - */ -DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds) -{ - var replacements = this._getReplacementsByContactIds(pContactIds); - var emailObj = {}; - for (let contactId in replacements) - { - if (this.type == DocumentTemplate.types.EML) - { - //use the special function for EML to also fill subject and sender - emailObj[contactId] = this._getReplacedEML(replacements[contactId], true); - } - else - { - let body = this.getReplacedContent(replacements[contactId]); - if (this.type == DocumentTemplate.types.TXT) - body = text.replaceAll(body, {"\n" : "<br>"}); - emailObj[contactId] = new Email(null, null, null, body); - } - } - return emailObj; -} - -/** - * Builds an object with the placeholder data for multiple contacts - * - * @param {Array} pContactIds contact ids - * - * @return {Object} Object containing the data. The structure is like {contactId : {placeholderName : replacementValue, ...}, ...} - * - * @private - */ -DocumentTemplate.prototype._getReplacementsByContactIds = function (pContactIds) -{ - var config = this._getRequiredPlaceholders(); - var contactIdPlaceholder = new Placeholder("contactId", Placeholder.types.SQLPART, "CONTACT.CONTACTID"); - config = [contactIdPlaceholder].concat(config); - var addressData = getAddressesData(pContactIds, config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection - var replacements = {}; - var placeholderNames = addressData[0]; - var contactIdIndex = placeholderNames.indexOf(contactIdPlaceholder.toString()); - for (let i = 1; i < addressData.length; i++) - { - let contactId = addressData[i][contactIdIndex]; - for (let ii = 0, ll = placeholderNames.length; ii < ll; ii++) - { - if (!(contactId in replacements)) - replacements[contactId] = {}; - replacements[contactId][placeholderNames[ii]] = addressData[i][ii]; - } - } - return replacements; -} - -/** - * Replaces placeholders for EML - * - * @param {Object} pReplacements mapping with replacements for every placeholder - * @param {boolean} [pGetEmail] if true, return an Email object (use this if the sender and subject are required) - * - * @return {String|Email} the replaced content - * - * @private - */ -DocumentTemplate.prototype._getReplacedEML = function (pReplacements, pGetEmail) -{ - var mailData = mail.parseRFC(util.decodeBase64String(this.content)); - var email; - var body = DocumentTemplate._replaceText(mailData[mail.MAIL_HTMLTEXT], pReplacements); - if (pGetEmail) - { - var sender = DocumentTemplate._replaceText(mailData[mail.MAIL_SENDER], pReplacements); - var subject = DocumentTemplate._replaceText(mailData[mail.MAIL_SUBJECT], pReplacements); - email = new Email(null, sender, subject, body); - } - else - email = body; - return email; -} - -/* - * replaces a given Odt-File on the server and returns the replaced base64-file - * - * @param {Object} pReplacements map of placeholders and replacements - * - * @return {String} base64-encoded replaced file - * - * @private - */ -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; - - /** - * replaces placeholders in a odt file - * - * @param {Object} pReplacements replacement object - * @param {String} pODTFileName filename of the odt - * - * @return {Boolean} - */ - function _replaceODTFile (pReplacements, pODTFileName) - { - var senderRelId = EmployeeUtils.getCurrentContactId(); - if (senderRelId == null) - return false; - if (pReplacements.length === undefined) - pReplacements = [pReplacements]; - if (pReplacements.length !== 0) - { - //replace placeholders in content.xml - var contentXml = util.decodeBase64String(pack.getFromZip(pODTFileName, "content.xml")); - var bodybegin = contentXml.indexOf("<office:body>"); - var bodyend = contentXml.indexOf("</office:body>") + 14; - - var bodyTemplate = contentXml.substring(bodybegin, bodyend); - var fullBody = ""; //body that contains all pages (required when the replacing is done for several contacts) - var beforeBody = contentXml.substring(0, bodybegin); - var afterBody = contentXml.substr(bodyend); - - for (let i = 0, l = pReplacements.length; i < l; i++) - { - let replacements = pReplacements[i]; - let currentBody = bodyTemplate; - - /* This only works if the text of the placeholders in the odt were not edited since they were written. - * If you edit the odt and change a placeholder (for example: you change '{@addres@}' to '{@address@}'), - * the text is saved in different XML tags and won't be replaced correctly. - */ - - for (let placeholder in replacements) - { - currentBody = currentBody.replace(placeholder, - replacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "ig"); - } - - fullBody += currentBody; - } - //TODO: TableData -// for (ti = 0; ti < pTableData.length; ti++) -// { -// var tablepos = bulkbody.indexOf( getDefaultODTplaceholer(pTableData[ti].Table, true)); -// if ( tablepos != -1 ) -// { -// var tablebegin = bulkbody.lastIndexOf("<table:table-row", tablepos); -// var tableend = bulkbody.indexOf("</table:table-row>", tablepos ) + 18; -// var lasttable = bulkbody.substr( tableend ); -// var tablerow = bulkbody.substring( tablebegin, tableend ); -// bulkbody = bulkbody.substring( 0, tablebegin ); -// var tabledata = pTableData[ti].TableData[addrdata[i][0]]; -// if ( tabledata != undefined ) -// { -// for (var tz = 0; tz < tabledata.length; tz++) -// { -// var table = tablerow; -// bulkbody += relaceAdditionValues( table, pTableData[ti].Fields, tabledata[tz], pTableData[ti].Table + "." ); -// } -// } -// bulkbody += lasttable; -// } - - - contentXml = beforeBody + fullBody + afterBody; - pack.addToZip(pODTFileName, "content.xml", util.encodeBase64String(contentXml)); - - //replace placeholders in styles.xml - var styles = util.decodeBase64String(pack.getFromZip(pODTFileName, "styles.xml")); - for (let placeholder in pReplacements[0]) - { - styles = styles.replace(placeholder, - pReplacements[0][placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "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 - * - * @private - */ -DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements) -{ - var replacements = {}; - for (let placeholder in pReplacements) //removes the prefix and postfix, the process needs it like this - 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; -} - -/** - * functions for working with letters (mails) - */ -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"); +import("Placeholder_lib"); +import("Email_lib"); + +/** + * Object for working with document templates, holds the content and type of the template. + * Provides functions to replace placeholders in the content. + * + * @class + */ +function DocumentTemplate (pTemplateContent, pType, pFilename) +{ + this.content = pTemplateContent; + this.type = pType; + this.filename = pFilename; +} + +DocumentTemplate.prototype.toString = function () +{ + if (this.type == DocumentTemplate.types.PLAIN) + return this.content; + return text.parseDocument(this.content); +} + +/** + * The types a DocumentTemplate can have. Depending on the type, + * the correct method for replacing the placeholders can be chosen + * + * @enum {String} + */ +DocumentTemplate.types = { + TXT : "txt", + HTML : "html", + EML : "eml", + ODT : "odt", + DOCX : "docx", + PLAIN : "plain" //for simple strings +}; + +/** + * Loads the content of a document template and creates a new DocumentTemplate object with that. + * + * @param {String} pAssignmentRowId id of the assignment (in most cases the document template id) + * @param {String} [pAssignmentTable="DOCUMENTTEMPLATE"] the LOB assignment table + * + * @return {DocumentTemplate} template object + * + * @throws {Error} if the type can't be used + */ +DocumentTemplate.loadTemplate = function (pAssignmentRowId, pAssignmentTable) +{ + var alias = "_____SYSTEMALIAS"; + if (!pAssignmentTable) + pAssignmentTable = "DOCUMENTTEMPLATE"; + var templateDocument = db.getBinaryMetadata(pAssignmentTable, "DOCUMENT", pAssignmentRowId, false, alias, null); + if (!templateDocument[0]) + return new DocumentTemplate(); + var binaryId = templateDocument[0][db.BINARY_ID]; + var filename = templateDocument[0][db.BINARY_FILENAME]; + var mimetype = templateDocument[0][db.BINARY_MIMETYPE]; + var typeMap = { + "text/plain" : DocumentTemplate.types.TXT, + "text/html" : DocumentTemplate.types.HTML, + "message/rfc822" : DocumentTemplate.types.EML, + "application/vnd.oasis.opendocument.text" : DocumentTemplate.types.ODT, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : DocumentTemplate.types.DOCX + }; + var type = typeMap[mimetype]; + if (type === undefined) + throw new Error("Invalid mime type for document template"); + + return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename); +} + +/** + * Replace function that works with strings instead of regular expressions + * so that control characters (for example '{', '}') don't have to be escaped. + * + * @private + */ +DocumentTemplate._replaceText = function (pText, pReplacements) +{ + for (let placeholder in pReplacements) + pText = pText.replace(placeholder, pReplacements[placeholder], "ig"); + return pText; +} + +/** + * returns the 'simpleName' of all placeholders that are used in the template + * + * @private + */ +DocumentTemplate.prototype._getRequiredPlaceholders = function () +{ + var allPlaceholders = PlaceholderUtils.getPlaceholders(); + var plainText = this.toString(); + var usedPlaceholders = []; + for (let i = 0, l = allPlaceholders.length; i < l; i++) + { + if (plainText.indexOf(allPlaceholders[i]) !== -1) + usedPlaceholders.push(allPlaceholders[i]); + } + return usedPlaceholders; +} + +/** + * 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); + case DocumentTemplate.types.PLAIN: + return DocumentTemplate._replaceText(this.content, pReplacements); + default: + return null; + } +} + +/** + * replaces the placeholders with data from one contact and returns the result + */ +DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) +{ + var replacements = this._getReplacementsByContactIds([pContactId]); + return this.getReplacedContent(replacements[pContactId]); +} + +/** + * replaces the placeholders with data from the contacts and returns the result + * + * @param {Array} pContactIds contact ids + * + * @return {Object} replaced content for every contactId + */ +DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds) +{ + var replacements = this._getReplacementsByContactIds(pContactIds); + var contents = {}; + for (let contactId in replacements) + { + contents[contactId] = this.getReplacedContent(replacements[contactId]); + } + return contents; +} + +/** + * Replaces the placeholders with data from the contacts and returns a serial letter, works + * only for ODT + * + * @param {Array} pContactIds contact ids + * + * @return {Object} the content of the replaced ODT + */ +DocumentTemplate.prototype.getSerialLetterByContactIds = function (pContactIds) +{ + var replacements = this._getReplacementsByContactIds(pContactIds); + if (this.type == DocumentTemplate.types.ODT) + { + let replaceArray = []; + for (let i = 0, l = pContactIds.length; i < l; i++) + for (let ii = 0; ii < 1000; ii++) + replaceArray.push(replacements[pContactIds[i]]); + return this._getReplacedODT(replaceArray); + } + return null; +} + +/** + * Replaces the placeholders with data from the contacts and returns the resulting Emails. + * + * @param {Array} pContactIds contact ids + * + * @return {Object} Object containing the contact ids as keys and the corresponding Email + * objects as values + */ +DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds) +{ + var replacements = this._getReplacementsByContactIds(pContactIds); + var emailObj = {}; + for (let contactId in replacements) + { + if (this.type == DocumentTemplate.types.EML) + { + //use the special function for EML to also fill subject and sender + emailObj[contactId] = this._getReplacedEML(replacements[contactId], true); + } + else + { + let body = this.getReplacedContent(replacements[contactId]); + if (this.type == DocumentTemplate.types.TXT) + body = text.replaceAll(body, {"\n" : "<br>"}); + emailObj[contactId] = new Email(null, null, null, body); + } + } + return emailObj; +} + +/** + * Builds an object with the placeholder data for multiple contacts + * + * @param {Array} pContactIds contact ids + * + * @return {Object} Object containing the data. The structure is like {contactId : {placeholderName : replacementValue, ...}, ...} + * + * @private + */ +DocumentTemplate.prototype._getReplacementsByContactIds = function (pContactIds) +{ + var config = this._getRequiredPlaceholders(); + var contactIdPlaceholder = new Placeholder("contactId", Placeholder.types.SQLPART, "CONTACT.CONTACTID"); + config = [contactIdPlaceholder].concat(config); + var addressData = getAddressesData(pContactIds, config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection + var replacements = {}; + var placeholderNames = addressData[0]; + var contactIdIndex = placeholderNames.indexOf(contactIdPlaceholder.toString()); + for (let i = 1; i < addressData.length; i++) + { + let contactId = addressData[i][contactIdIndex]; + for (let ii = 0, ll = placeholderNames.length; ii < ll; ii++) + { + if (!(contactId in replacements)) + replacements[contactId] = {}; + replacements[contactId][placeholderNames[ii]] = addressData[i][ii]; + } + } + return replacements; +} + +/** + * Replaces placeholders for EML + * + * @param {Object} pReplacements mapping with replacements for every placeholder + * @param {boolean} [pGetEmail] if true, return an Email object (use this if the sender and subject are required) + * + * @return {String|Email} the replaced content + * + * @private + */ +DocumentTemplate.prototype._getReplacedEML = function (pReplacements, pGetEmail) +{ + var mailData = mail.parseRFC(util.decodeBase64String(this.content)); + var email; + var body = DocumentTemplate._replaceText(mailData[mail.MAIL_HTMLTEXT], pReplacements); + if (pGetEmail) + { + var sender = DocumentTemplate._replaceText(mailData[mail.MAIL_SENDER], pReplacements); + var subject = DocumentTemplate._replaceText(mailData[mail.MAIL_SUBJECT], pReplacements); + email = new Email(null, sender, subject, body); + } + else + email = body; + return email; +} + +/* + * replaces a given Odt-File on the server and returns the replaced base64-file + * + * @param {Object} pReplacements map of placeholders and replacements + * + * @return {String} base64-encoded replaced file + * + * @private + */ +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; + + /** + * replaces placeholders in a odt file + * + * @param {Object} pReplacements replacement object + * @param {String} pODTFileName filename of the odt + * + * @return {Boolean} + */ + function _replaceODTFile (pReplacements, pODTFileName) + { + var senderRelId = EmployeeUtils.getCurrentContactId(); + if (senderRelId == null) + return false; + if (pReplacements.length === undefined) + pReplacements = [pReplacements]; + if (pReplacements.length !== 0) + { + //replace placeholders in content.xml + var contentXml = util.decodeBase64String(pack.getFromZip(pODTFileName, "content.xml")); + var bodybegin = contentXml.indexOf("<office:body>"); + var bodyend = contentXml.indexOf("</office:body>") + 14; + + var bodyTemplate = contentXml.substring(bodybegin, bodyend); + var fullBody = ""; //body that contains all pages (required when the replacing is done for several contacts) + var beforeBody = contentXml.substring(0, bodybegin); + var afterBody = contentXml.substr(bodyend); + + for (let i = 0, l = pReplacements.length; i < l; i++) + { + let replacements = pReplacements[i]; + let currentBody = bodyTemplate; + + /* This only works if the text of the placeholders in the odt were not edited since they were written. + * If you edit the odt and change a placeholder (for example: you change '{@addres@}' to '{@address@}'), + * the text is saved in different XML tags and won't be replaced correctly. + */ + + for (let placeholder in replacements) + { + currentBody = currentBody.replace(placeholder, + replacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "ig"); + } + + fullBody += currentBody; + } + //TODO: TableData +// for (ti = 0; ti < pTableData.length; ti++) +// { +// var tablepos = bulkbody.indexOf( getDefaultODTplaceholer(pTableData[ti].Table, true)); +// if ( tablepos != -1 ) +// { +// var tablebegin = bulkbody.lastIndexOf("<table:table-row", tablepos); +// var tableend = bulkbody.indexOf("</table:table-row>", tablepos ) + 18; +// var lasttable = bulkbody.substr( tableend ); +// var tablerow = bulkbody.substring( tablebegin, tableend ); +// bulkbody = bulkbody.substring( 0, tablebegin ); +// var tabledata = pTableData[ti].TableData[addrdata[i][0]]; +// if ( tabledata != undefined ) +// { +// for (var tz = 0; tz < tabledata.length; tz++) +// { +// var table = tablerow; +// bulkbody += relaceAdditionValues( table, pTableData[ti].Fields, tabledata[tz], pTableData[ti].Table + "." ); +// } +// } +// bulkbody += lasttable; +// } + + + contentXml = beforeBody + fullBody + afterBody; + pack.addToZip(pODTFileName, "content.xml", util.encodeBase64String(contentXml)); + + //replace placeholders in styles.xml + var styles = util.decodeBase64String(pack.getFromZip(pODTFileName, "styles.xml")); + for (let placeholder in pReplacements[0]) + { + styles = styles.replace(placeholder, + pReplacements[0][placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "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 + * + * @private + */ +DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements) +{ + var replacements = {}; + for (let placeholder in pReplacements) //removes the prefix and postfix, the process needs it like this + 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; +} + +/** + * functions for working with letters (mails) + */ +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/buildSerialLetter_serverProcess/buildSerialLetter_serverProcess.aod b/process/buildSerialLetter_serverProcess/buildSerialLetter_serverProcess.aod new file mode 100644 index 0000000000000000000000000000000000000000..0cfcaabc36e1978d969d6e4543408803fb9d1004 --- /dev/null +++ b/process/buildSerialLetter_serverProcess/buildSerialLetter_serverProcess.aod @@ -0,0 +1,10 @@ +<?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>buildSerialLetter_serverProcess</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/buildSerialLetter_serverProcess/process.js</process> + <alias>Data_alias</alias> + <variants> + <element>EXECUTABLE</element> + </variants> +</process> diff --git a/process/buildSerialLetter_serverProcess/process.js b/process/buildSerialLetter_serverProcess/process.js new file mode 100644 index 0000000000000000000000000000000000000000..2ad448bad7ed33c636a69df3b55c9798fc76dcad --- /dev/null +++ b/process/buildSerialLetter_serverProcess/process.js @@ -0,0 +1,25 @@ +import("system.logging"); +import("system.result"); +import("system.vars"); +import("KeywordRegistry_basic"); +import("Contact_lib"); +import("Sql_lib"); +import("system.db"); +import("system.neon"); +import("DocumentTemplate_lib"); + +var letterId = vars.get("$local.serialLetterId"); +var templateId = db.cell(SqlCondition.begin() + .andPrepare("SERIALLETTER.SERIALLETTERID", letterId) + .buildSql("select DOCUMENTTEMPLATE_ID from SERIALLETTER", "1=2")); + +var template = DocumentTemplate.loadTemplate(templateId); +var contactIds = db.table(SqlCondition.begin() + .andPrepare("LETTERRECIPIENT.SERIALLETTER_ID", letterId) + .andSqlCondition(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$letter(), true)) + .buildSql("select CONTACT_ID from LETTERRECIPIENT join CONTACT on LETTERRECIPIENT.CONTACT_ID = CONTACT.CONTACTID", "1=2") +); +var document = template.getSerialLetterByContactIds(contactIds); +logging.log("ja") +if (document) + neon.download(document, template.filename); \ No newline at end of file diff --git a/process/sendBulkMail_serverProcess/process.js b/process/sendBulkMail_serverProcess/process.js index 0403cb3f69f8223ece7216225bf9f0518720fa78..68c0371b37a3721cd13e0aef3113a8e170034ecd 100644 --- a/process/sendBulkMail_serverProcess/process.js +++ b/process/sendBulkMail_serverProcess/process.js @@ -1,25 +1,25 @@ -import("system.datetime"); -import("Sql_lib"); -import("system.db"); -import("system.util"); -import("system.translate"); -import("Bulkmail_lib"); -import("system.vars"); -import("system.notification"); - -var startTime = datetime.date(); -var bulkMailId = vars.get("$local.bulkMailId"); -var user = vars.get("$local.user"); -var res = BulkMailUtils.sendBulkMail(bulkMailId); - -if (user) -{ - var mailName = db.cell(SqlCondition.begin() - .andPrepare("BULKMAIL.BULKMAILID", bulkMailId) - .buildSql("select NAME from BULKMAIL") - ); - var message = translate.withArguments("Bulk mail \"%0\" was sent!", [mailName]); - var description = translate.withArguments("%0 mails sent sucessfully, %1 mails failed. Process took %2 s.", - [res.sucessful, res.failed, Math.round((datetime.date() - startTime) / datetime.ONE_SECOND)]); - notification.addNotification(util.getNewUUID(), null, null, null, "BulkMailSent", notification.PRIO_NORMAL, 2, notification.STATE_UNSEEN, [user], message, description); +import("system.datetime"); +import("Sql_lib"); +import("system.db"); +import("system.util"); +import("system.translate"); +import("Bulkmail_lib"); +import("system.vars"); +import("system.notification"); + +var startTime = datetime.date(); +var bulkMailId = vars.get("$local.bulkMailId"); +var user = vars.get("$local.user"); +var res = BulkMailUtils.sendBulkMail(bulkMailId); + +if (user) +{ + var mailName = db.cell(SqlCondition.begin() + .andPrepare("BULKMAIL.BULKMAILID", bulkMailId) + .buildSql("select NAME from BULKMAIL") + ); + var message = translate.withArguments("Bulk mail \"%0\" was sent!", [mailName]); + var description = translate.withArguments("%0 mails sent sucessfully, %1 mails failed. Process took %2 s.", + [res.sucessful, res.failed, Math.round((datetime.date() - startTime) / datetime.ONE_SECOND)]); + notification.addNotification(util.getNewUUID(), null, null, null, "BulkMailSent", notification.PRIO_NORMAL, 2, notification.STATE_UNSEEN, [user], message, description); } \ No newline at end of file