diff --git a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js index 4aa2fc2aac11e228e042b757019152b6ee802d5f..b71a637ad5f3e5f63751e823d8acaa560e4a7a87 100644 --- a/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js +++ b/entity/Attribute_entity/recordcontainers/jdito/contentProcess.js @@ -1,4 +1,5 @@ import("system.logging"); +import("system.datetime"); import("system.translate"); import("Util_lib"); import("JditoFilter_lib"); @@ -10,20 +11,24 @@ import("system.result"); import("Sql_lib"); import("Attribute_lib"); +var t = datetime.date() + 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"); - -logging.log([getGroups, objectType, parentType]) +var fetchUsages = true; 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 " - + "from AB_ATTRIBUTE UIDROW " - + "left join AB_ATTRIBUTE PARENT1 on UIDROW.ATTRIBUTE_PARENT_ID = PARENT1.AB_ATTRIBUTEID " //always select the names of the next 3 parents so that less queries - + "left join AB_ATTRIBUTE PARENT2 ON PARENT1.ATTRIBUTE_PARENT_ID = PARENT2.AB_ATTRIBUTEID " //are required later when buildung the full name - + "left join AB_ATTRIBUTE PARENT3 ON PARENT2.ATTRIBUTE_PARENT_ID = PARENT3.AB_ATTRIBUTEID"; + + ", '', 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"; @@ -69,18 +74,7 @@ else if (objectType) //if there's an objectType, it comes from the AttributeRel else // do not return anything, if parameter is there but an empty array condition.and("1=2"); - -} -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"); + fetchUsages = false; } //when there are filters selected, add them to the conditon @@ -91,21 +85,26 @@ if (vars.exists("$local.filter") && vars.get("$local.filter")) condition.andSqlCondition(JditoFilterUtils.getSqlCondition(filter.filter, "AB_ATTRIBUTE", uidTableAlias)); } -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")); -var usages = {}; -for (let i = 0, l = usageTbl.length; i < l; i++) +var usages; +if (fetchUsages) //this query is only necessary in Attribute, not in AttributeRelation { - let attrId = usageTbl[i][0]; - if (attrId in usages) - usages[attrId].push(usageTbl[i][1]); - else - usages[attrId] = [usageTbl[i][1]]; + 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)); @@ -140,7 +139,7 @@ function _buildAttributeTable (pAttributes, pUsages) for (let i in rows) { let rowData = rows[i].data; - if (rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) + if (pUsages && rowData[5].trim() != $AttributeTypes.COMBOVALUE && i in pUsages) { rowData[7] = pUsages[i].map(function (usage) { @@ -163,8 +162,17 @@ function _buildAttributeTable (pAttributes, pUsages) */ function _getFullName (pAttributeName, pParent1Name, pParent2Name, pParent3Name, pParent4Id) { - var parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id) : null; + var parent4FullName; + if (pParent4Id && pParent4Id in nameCache) + parent4FullName = nameCache[pParent4Id]; + else + { + parent4FullName = pParent4Id ? AttributeUtil.getFullAttributeName(pParent4Id) : null; + nameCache[pParent4Id] = parent4FullName; + } pAttributeName = ArrayUtils.joinNonEmptyFields([parent4FullName, pParent3Name, pParent2Name, pParent1Name, pAttributeName], " / "); return pAttributeName; -} \ No newline at end of file +} + +logging.log(datetime.date() - t) \ No newline at end of file diff --git a/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js b/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js index 4fcb6ca5a0ce8770d33d6201b8822ebca6553037..0dd257f7f36ea76b1dea44e0405d5105a408a75d 100644 --- a/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js +++ b/entity/Offer_entity/entityfields/lettersalutation/valueProcess.js @@ -2,7 +2,7 @@ import("system.logging"); import("system.result"); import("system.neon"); import("system.vars"); -import("PostalAddress_lib"); +import("Address_lib"); if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && !vars.get("$this.value")) { diff --git a/process/Bulkmail_lib/Bulkmail_lib.aod b/process/Bulkmail_lib/Bulkmail_lib.aod index e02358029db1060e0396ba5d77f7b402cf4d7d27..3181ac4567c83ec13a2f9438ce34c5c9957bc113 100644 --- a/process/Bulkmail_lib/Bulkmail_lib.aod +++ b/process/Bulkmail_lib/Bulkmail_lib.aod @@ -1,6 +1,6 @@ <?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>Bulkmail_lib</name> + <name>BulkMail_lib</name> <majorModelMode>DISTRIBUTED</majorModelMode> <process>%aditoprj%/process/Bulkmail_lib/process.js</process> <variants> diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js index 27a4b8362f5d4c49ca9a2094eb1fab7919eddaa8..49290ffaef9ab4092b94476882052e1aaf7b0865 100644 --- a/process/Bulkmail_lib/process.js +++ b/process/Bulkmail_lib/process.js @@ -1,6 +1,52 @@ +import("KeywordRegistry_basic"); +import("Sql_lib"); +import("system.db"); import("DocumentTemplate_lib"); +import("Email_lib"); -function Bulkmail () + +function BulkMailUtils () {} + +BulkMailUtils.sendBulkMail = function (pBulkMailId) { + var templateId = db.cell(SqlCondition.begin() + .andPrepare("BULKMAIL.BULKMAILID", pBulkMailId) + .buildSql("select DOCUMENTTEMPLATE_ID from BULKMAIL", "1=2") + ); + var template = DocumentTemplate.loadTemplate(templateId); + var emailSender; + + var recipientData = db.table(SqlCondition.begin() + .andPrepare("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) + //TODO: more condition + .buildSql("select BULKMAILRECIPIENTID, CONTACT_ID, '' from BULKMAILRECIPIENT", "1=2") + ); + + var successIds = []; + var failedIds = []; + var mails = template.getReplacedEmailsByContactIds(recipientData.map(function (e) {return e[0];})); + for (let i = 0, l = recipientData.length; i < l; i++) + { + let contactId = recipientData[i][1]; + let email = mails[contactId]; + email.toRecipients = [recipientData[i][1]]; + email.sender = emailSender; + + let 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"], null, [$KeywordRegistry.bulkMailSentStatus$sent()], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds) + .build("1=2") + ); + db.updateData("BULKMAILRECIPIENT", ["STATUS"], null, [$KeywordRegistry.bulkMailSentStatus$failed()], + SqlCondition.begin() + .andIn("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds) + .build("1=2") + ); } \ No newline at end of file diff --git a/process/DocumentTemplate_lib/process.js b/process/DocumentTemplate_lib/process.js index 2cbeffcce9962843b8873fbbab9a2db3a4efd75d..d4c01e318b614e7fedbb43822aac0bdf8c1db096 100644 --- a/process/DocumentTemplate_lib/process.js +++ b/process/DocumentTemplate_lib/process.js @@ -1,4 +1,3 @@ -import("system.logging"); import("Communication_lib"); import("system.neon"); import("Employee_lib"); @@ -147,67 +146,103 @@ DocumentTemplate.prototype.getReplacedContent = function (pReplacements) /** * replaces the placeholders with data from the contact and returns the result */ -DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) //TODO: function required for mass replacing (for bulkmails) +DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId) { - var replacements = this._getReplacementsByContactId(pContactId); - return this.getReplacedContent(replacements); + var replacements = this._getReplacementsByContactIds([pContactId]); + return this.getReplacedContent(replacements[pContactId]); } /** - * @private + * 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._getReplacementsByContactId = function (pContactId) +DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds) { - var config = this._getRequiredPlaceholders(); - var addressData = getAddressesData([pContactId], config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection - var replacements = {}; - for (let i = 0, l = addressData[0].length; i < l; i++) + var replacements = this._getReplacementsByContactIds(pContactIds); + var contents = {}; + for (let contactId in replacements) { - replacements[addressData[0][i]] = addressData[1][i]; + contents[contactId] = this.getReplacedContent(replacements[contactId]); } - return replacements; + return contents; } /** - * loads data into an Email object from a template + * replaces the placeholders with data from the contacts and returns the result * - * @param {Email} pEmail email object to change - * @param {String} pContactId contactId of the recipient + * @param {Array} pContactIds contact ids + * + * @return {Object} emails for every contactId */ -DocumentTemplate.prototype.setEmailTemplateByContactId = function (pEmail, pContactId) +DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds) { - if (this.type != DocumentTemplate.types.EML && this.type != DocumentTemplate.types.HTML && this.type != DocumentTemplate.types.TXT) - throw new Error("Invalid document type for an email template"); - - var replacements = DocumentTemplate._getReplacementsByContactId(pContactId); - - if (this.type == DocumentTemplate.types.EML) //special treatment because eml contains more information than just the body + var replacements = this._getReplacementsByContactIds(pContactIds); + var emailObj = {}; + for (let contactId in replacements) { - let email = mail.parseRFC(util.decodeBase64String(this.content)); - pEmail.sender = DocumentTemplate._replaceText(email[mail.MAIL_SENDER], replacements); - pEmail.subject = DocumentTemplate._replaceText(email[mail.MAIL_SUBJECT], replacements); - pEmail.body = DocumentTemplate._replaceText(email[mail.MAIL_HTMLTEXT], replacements); + if (this.type == DocumentTemplate.types.EML) + emailObj[contactId] = this._getReplacedEML(replacements[contactId], true); + else + { + let body = this.getReplacedContent(replacements[contactId]); + emailObj[contactId] = new Email(null, null, null, body); + } } - else - pEmail.body = this.getReplacedContent(replacements); - + return emailObj; } /** - * Replaces placeholders for EML. This function only works for the email body, - * so if you also need the subject, sender, etc use DocumentTemplate.prototype.setEmailTemplateByContactId. + * @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 Email object * - * @return {String} the replaced content + * @return {String|Email} the replaced content * * @private */ -DocumentTemplate.prototype._getReplacedEML = function (pReplacements) +DocumentTemplate.prototype._getReplacedEML = function (pReplacements, pGetEmail) { - var email = mail.parseRFC(util.decodeBase64String(this.content)); - var htmlText = email[mail.MAIL_HTMLTEXT]; - return DocumentTemplate._replaceText(htmlText, pReplacements); + 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; } /* @@ -297,7 +332,7 @@ DocumentTemplate.prototype._getReplacedODT = function (pReplacements) DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements) { var replacements = {}; - for (let placeholder in pReplacements) + 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 diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js index 5047809e526692897da79d968059aad4a0325d89..bf786a48d2025baa41d5c9ba7045d26a5cc27c6e 100644 --- a/process/Email_lib/process.js +++ b/process/Email_lib/process.js @@ -24,9 +24,9 @@ function EmailUtils () {} EmailUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId) { var email = new Email(pToRecipients); - email.setSender(pSenderContactId); if (pTemplateId) email.setTemplate(pTemplateId, pRecipientContactId); + email.setSender(pSenderContactId); email.downloadEML(); } @@ -74,13 +74,14 @@ function Email (pToRecipients, pSender, pSubject, pBody, pCcRecipients, pBccReci * * @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); - template.setEmailTemplateByContactId(this, pContactId); + var email = template.getReplacedEmailsByContactIds([pContactId])[pContactId]; + this.sender = email.sender; + this.body = email.body; + this.subject = email.subject; } /** @@ -186,4 +187,52 @@ Email.prototype.downloadEML = function() { neon.download(util.encodeBase64String(this.getRFCmail(), null), (this.subject || translate.text("Email Template")) + ".eml"); } - \ No newline at end of file + +/** + * sends the email object + * + * @return {boolean} true, if the mail was sent sucessfully + */ +Email.prototype.send = function () +{ + var ENCODING = "UTF-8"; + var mailId; + try + { + mailId = mail.newMail(); + } + catch(ex) + { + //TODO: fix this dirty workaround [waiting for #1038963], since newMail causes an error on the first call after a user logged in + logging.log(ex); + util.sleep(1500); + 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); + + try + { + mail.sendMail(mailId); + return true; + } + catch (ex) + { + return false; + } +} \ No newline at end of file diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index 3c087550b0af4dbc39812ac5b9f608ccf78bfd2c..276c521b0f52d3de505491a4868c559016d27c28 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -107,3 +107,8 @@ $KeywordRegistry.permissionCondType = function(){return "PermissionCondType";}; $KeywordRegistry.permissionAccessType = function(){return "PermissionAccessType";}; $KeywordRegistry.communicationMediumCampaign = function(){return "CommunicationMediumCampaign";}; + +$KeywordRegistry.bulkMailSentStatus = function(){return "BulkMailSentStatus";}; +$KeywordRegistry.bulkMailSentStatus$pending = function(){return "9a0c5608-070e-49fb-92cd-f6abece9242d";}; +$KeywordRegistry.bulkMailSentStatus$sent = function(){return "147211fb-a1cf-49c8-8e08-c3cfe0404f9b";}; +$KeywordRegistry.bulkMailSentStatus$failed = function(){return "353e27e9-7491-4bfd-b9f9-f18f2cb2a36c";}; \ No newline at end of file