diff --git a/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/alter_emailFilterHandling.xml b/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/alter_emailFilterHandling.xml new file mode 100644 index 0000000000000000000000000000000000000000..52178da7d2889205e3ac9ceae47230b2360896af --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/alter_emailFilterHandling.xml @@ -0,0 +1,11 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="s.listl" id="5ba15adf-e9c9-46ee-989e-7ee02a3a61b7"> + <addColumn tableName="EMAIL_FILTER_HANDLING"> + <column name="ACTION_TYPE" type="VARCHAR(36)"/> + <column name="WORKFLOWSIGNAL_NAME" type="NVARCHAR(250)"/> + </addColumn> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/insert_recipientStatusBounced.xml b/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/insert_recipientStatusBounced.xml new file mode 100644 index 0000000000000000000000000000000000000000..715254b5612bb7e425a511dd133705e0d7c0bc71 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.1.1/EmailFilter/insert_recipientStatusBounced.xml @@ -0,0 +1,27 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <changeSet author="s.listl" id="286b9086-b5a3-4ffc-b747-c8836d874666"> + <insert tableName="AB_KEYWORD_ENTRY"> + <column name="AB_KEYWORD_ENTRYID" value="f79c7bad-9ff5-45d5-b93e-4ce52bf88c2d"/> + <column name="AB_KEYWORD_CATEGORY_ID" value="25cb446a-24cd-4ebd-aad2-320da20830da"/> + <column name="KEYID" value="EMAILBOUNCED_SOFT"/> + <column name="TITLE" value="Bounce (Soft)"/> + <column name="CONTAINER" value="BulkMailRecipientStatus"/> + <column name="SORTING" valueNumeric="4"/> + <column name="ISACTIVE" valueNumeric="1"/> + <column name="ISESSENTIAL" valueNumeric="1"/> + </insert> + <insert tableName="AB_KEYWORD_ENTRY"> + <column name="AB_KEYWORD_ENTRYID" value="f42932ab-935a-46e9-957c-dd4e7f82daa8"/> + <column name="AB_KEYWORD_CATEGORY_ID" value="25cb446a-24cd-4ebd-aad2-320da20830da"/> + <column name="KEYID" value="EMAILBOUNCED_HARD"/> + <column name="TITLE" value="Bounce (Hard)"/> + <column name="CONTAINER" value="BulkMailRecipientStatus"/> + <column name="SORTING" valueNumeric="5"/> + <column name="ISACTIVE" valueNumeric="1"/> + <column name="ISESSENTIAL" valueNumeric="1"/> + </insert> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.1.1/changelog.xml b/.liquibase/Data_alias/basic/2021.1.1/changelog.xml new file mode 100644 index 0000000000000000000000000000000000000000..dbb03bc9d62c0fea077c10159291a3f25f764e5b --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.1.1/changelog.xml @@ -0,0 +1,7 @@ +<?xml version="1.1" encoding="UTF-8" standalone="no"?> +<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"> + <include relativeToChangelogFile="true" file="EmailFilter/alter_emailFilterHandling.xml"/> + <include relativeToChangelogFile="true" file="EmailFilter/insert_recipientStatusBounced.xml"/> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/changelog.xml b/.liquibase/Data_alias/changelog.xml index 5266ede8f1709637ccf87f5edfeb27bf5b838afe..d2cd89c296487cbe7bfd4295b95211d16ad8e7b6 100644 --- a/.liquibase/Data_alias/changelog.xml +++ b/.liquibase/Data_alias/changelog.xml @@ -24,6 +24,7 @@ <include relativeToChangelogFile="true" file="basic/2021.0.2/changelog.xml"/> <include relativeToChangelogFile="true" file="basic/2021.0.3/changelog.xml"/> <include relativeToChangelogFile="true" file="basic/2021.1.0/changelog.xml"/> + <include relativeToChangelogFile="true" file="basic/2021.1.1/changelog.xml"/> <include relativeToChangelogFile="true" file="basic/workflows/changelog.xml" context="workflow"/> <!--enable this only when you definetly want to overwrite the existing data with demo records:--> diff --git a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod index 2f1a06e4716ada4abac58d06429cb80228e835ad..ee0a5b93039f68e19d4786c596e783eb21d5a6d6 100644 --- a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod +++ b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod @@ -226,6 +226,10 @@ <name>Interest</name> <kind v="10077" /> </entityNode> + <entityNode> + <name>EmailFilterHandling</name> + <kind v="10077" /> + </entityNode> </childNodes> </entityNode> <entityNode> diff --git a/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod b/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod index 7f856b4de39b2ae6cb730eeec8cc620eef1f0b79..4c13d49a9ff7c5b6a0871afa7cf64807c876a35c 100644 --- a/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod +++ b/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod @@ -161,12 +161,14 @@ <title>Use for test run</title> <onActionProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/testrunactions/children/settestrecipient/onActionProcess.js</onActionProcess> <isObjectAction v="false" /> + <iconId>VAADIN:CHECK</iconId> </entityActionField> <entityActionField> <name>removeTestRecipient</name> <title>Don't use for test run</title> <onActionProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/testrunactions/children/removetestrecipient/onActionProcess.js</onActionProcess> <isObjectAction v="false" /> + <iconId>VAADIN:CLOSE</iconId> </entityActionField> </children> </entityActionGroup> diff --git a/entity/BulkMail_entity/BulkMail_entity.aod b/entity/BulkMail_entity/BulkMail_entity.aod index 4dfdfce86f3537a69b0630d7c5b3431f6f27d6c4..f755fbfd886dd8b588903a8b66f1eb0b7c0a1a89 100644 --- a/entity/BulkMail_entity/BulkMail_entity.aod +++ b/entity/BulkMail_entity/BulkMail_entity.aod @@ -336,6 +336,7 @@ </entityConsumer> <entityConsumer> <name>BulkMailTestRecipients</name> + <selectionMode>MULTI</selectionMode> <stateProcess>%aditoprj%/entity/BulkMail_entity/entityfields/bulkmailtestrecipients/stateProcess.js</stateProcess> <dependency> <name>dependency</name> diff --git a/entity/EmailData_entity/EmailData_entity.aod b/entity/EmailData_entity/EmailData_entity.aod index 86a513a1b6ed57606b1ae1f10575f37be7aa905d..609ccf4bd184e3346ab653847da2acea52a8dcde 100644 --- a/entity/EmailData_entity/EmailData_entity.aod +++ b/entity/EmailData_entity/EmailData_entity.aod @@ -36,6 +36,10 @@ <name>BODY_REGEX</name> <title>Email body (Regular expression)</title> </entityField> + <entityField> + <name>SENDERROR</name> + <title>Error</title> + </entityField> </entityFields> <recordContainers> <jDitoRecordContainer> @@ -66,6 +70,10 @@ <name>ATTACHMENTCOUNT.value</name> <isFilterable v="true" /> </jDitoRecordFieldMapping> + <jDitoRecordFieldMapping> + <name>SENDERROR.value</name> + <isFilterable v="true" /> + </jDitoRecordFieldMapping> </recordFieldMappings> </jDitoRecordContainer> </recordContainers> diff --git a/entity/EmailFilterHandling_entity/EmailFilterHandling_entity.aod b/entity/EmailFilterHandling_entity/EmailFilterHandling_entity.aod index 2b3ec48be0b04a1e3f76b9818f7a2621134b5dc9..a177320afec6c7c7a03e357f04f79139ce1957e5 100644 --- a/entity/EmailFilterHandling_entity/EmailFilterHandling_entity.aod +++ b/entity/EmailFilterHandling_entity/EmailFilterHandling_entity.aod @@ -33,7 +33,7 @@ <name>WORKFLOWDEFINITION_KEY</name> <title>Workflow</title> <consumer>Workflows</consumer> - <displayValueProcess>%aditoprj%/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/displayValueProcess.js</displayValueProcess> + <stateProcess>%aditoprj%/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/stateProcess.js</stateProcess> </entityField> <entityConsumer> <name>FilterTypeKeyword</name> @@ -82,6 +82,7 @@ <entityField> <name>WORKFLOWSIGNAL_NAME</name> <title>Signal</title> + <stateProcess>%aditoprj%/entity/EmailFilterHandling_entity/entityfields/workflowsignal_name/stateProcess.js</stateProcess> </entityField> <entityField> <name>ACTION_TYPE</name> @@ -89,9 +90,24 @@ </entityField> <entityField> <name>ISFALLTHROUGH</name> + <title>Continue</title> <contentType>BOOLEAN</contentType> <valueProcess>%aditoprj%/entity/EmailFilterHandling_entity/entityfields/isfallthrough/valueProcess.js</valueProcess> </entityField> + <entityConsumer> + <name>ActionTypeKeyword</name> + <dependency> + <name>dependency</name> + <entityName>KeywordEntry_entity</entityName> + <fieldName>SpecificContainerKeywords</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContainerName_param</name> + <valueProcess>%aditoprj%/entity/EmailFilterHandling_entity/entityfields/actiontypekeyword/children/containername_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> </entityFields> <recordContainers> <dbRecordContainer> @@ -138,6 +154,13 @@ <name>ISFALLTHROUGH.value</name> <recordfield>EMAIL_FILTER_HANDLING.ISFALLTHROUGH</recordfield> </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>DESCRIPTION.value</name> + <recordfield>EMAIL_FILTER_HANDLING.DESCRIPTION</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>WORKFLOWSIGNAL_NAME.value</name> + </dbRecordFieldMapping> </recordFieldMappings> <linkInformation> <linkInformation> diff --git a/entity/EmailFilterHandling_entity/entityfields/actiontypekeyword/children/containername_param/valueProcess.js b/entity/EmailFilterHandling_entity/entityfields/actiontypekeyword/children/containername_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..e2c2914556c6e5c59be1bec07528c80d65f520db --- /dev/null +++ b/entity/EmailFilterHandling_entity/entityfields/actiontypekeyword/children/containername_param/valueProcess.js @@ -0,0 +1,4 @@ +import("KeywordRegistry_basic"); +import("system.result"); + +result.string($KeywordRegistry.weblinkActionType()); \ No newline at end of file diff --git a/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/displayValueProcess.js b/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/displayValueProcess.js deleted file mode 100644 index 9b4c511d8c7320136a7cee0848d48822b2c6ca75..0000000000000000000000000000000000000000 --- a/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/displayValueProcess.js +++ /dev/null @@ -1,5 +0,0 @@ -import("system.vars"); -import("system.result"); - -if (vars.get("$field.WORKFLOWDEFINITION_KEY")) -result.string("Werbeeinstellung setzen") \ No newline at end of file diff --git a/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/stateProcess.js b/entity/EmailFilterHandling_entity/entityfields/workflowdefinition_key/stateProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/entity/EmailFilterHandling_entity/entityfields/workflowsignal_name/stateProcess.js b/entity/EmailFilterHandling_entity/entityfields/workflowsignal_name/stateProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..a8892ce33aa94fa012dd1c30e32963eae278dbf6 --- /dev/null +++ b/entity/EmailFilterHandling_entity/entityfields/workflowsignal_name/stateProcess.js @@ -0,0 +1,7 @@ +import("system.result"); +import("system.vars"); +import("KeywordRegistry_basic"); +import("system.neon"); + +var actionType = vars.get("$field.ACTION_TYPE"); +result.string(actionType == $KeywordRegistry.weblinkActionType$sendWorkflowSignal() ? neon.COMPONENTSTATE_EDITABLE : neon.COMPONENTSTATE_INVISIBLE); \ No newline at end of file diff --git a/entity/EmailFilterHandling_entity/recordcontainers/db/orderClauseProcess.js b/entity/EmailFilterHandling_entity/recordcontainers/db/orderClauseProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..40b390ad77d32c93aff3ac8d280095ea8272ca85 --- /dev/null +++ b/entity/EmailFilterHandling_entity/recordcontainers/db/orderClauseProcess.js @@ -0,0 +1,6 @@ +import("system.db"); +import("system.result"); + +result.object({ + "PRIORITY": db.ASCENDING +}); \ No newline at end of file diff --git a/neonView/EmailFilterHandlingPreview_view/EmailFilterHandlingPreview_view.aod b/neonView/EmailFilterHandlingPreview_view/EmailFilterHandlingPreview_view.aod index cd1dc162ddf476a1e3b38f7844d4ee2d10c5bb1b..11d730a4ab6604c87094d7578136a707ce299fff 100644 --- a/neonView/EmailFilterHandlingPreview_view/EmailFilterHandlingPreview_view.aod +++ b/neonView/EmailFilterHandlingPreview_view/EmailFilterHandlingPreview_view.aod @@ -14,5 +14,28 @@ <titleField>TITLE</titleField> <subtitleField>FILTER_TYPE</subtitleField> </cardViewTemplate> + <genericViewTemplate> + <name>Generic</name> + <showDrawer v="true" /> + <drawerCaption>Details</drawerCaption> + <fields> + <entityFieldLink> + <name>a71ac88f-f0c3-4de7-9d1f-a989212bed71</name> + <entityField>ISACTIVE</entityField> + </entityFieldLink> + <entityFieldLink> + <name>6cbad6d9-8434-4328-8d82-6010240a5a7f</name> + <entityField>ISFALLTHROUGH</entityField> + </entityFieldLink> + <entityFieldLink> + <name>2adc5e19-553a-41e6-8bd0-40e3b7fc98f6</name> + <entityField>ACTION_TYPE</entityField> + </entityFieldLink> + <entityFieldLink> + <name>560c1795-60ff-4ea6-b17b-19f49610bc8f</name> + <entityField>WORKFLOWDEFINITION_KEY</entityField> + </entityFieldLink> + </fields> + </genericViewTemplate> </children> </neonView> diff --git a/process/BulkmailAnalysis_lib/process.js b/process/BulkmailAnalysis_lib/process.js index 7a8a4b42171a080a6cb25100abc09082aab9a5a1..6077fbb5e0119cee32fa70d3de2492baef0ace63 100644 --- a/process/BulkmailAnalysis_lib/process.js +++ b/process/BulkmailAnalysis_lib/process.js @@ -117,10 +117,9 @@ BulkMailAnalysisSql.countSelects = CLICKCOUNT:"count(distinct WEBLINK_CLICK.WEBLINK_CLICKID)", UNIQUECLICKCOUNT:"count(distinct WEBLINK_CLICK.MAIL_LOG_ID)", OPENERCOUNT:"count(distinct MAIL_LOG.OPENER_LINK_CLICK_ID)", - //todo: Keywordregistry verwenden wenn bounces mit drin sind. - BOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS in ('EMAILBOUNCED_HARD','EMAILBOUNCED_SOFT') then MAIL_LOG.MAIL_LOGID else null end)", - SOFTBOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS ='EMAILBOUNCED_SOFT' then MAIL_LOG.MAIL_LOGID else null end)", - HARDBOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS ='EMAILBOUNCED_HARD' then MAIL_LOG.MAIL_LOGID else null end)", + BOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS in ('"+$KeywordRegistry.bulkMailRecipientStatus$softBounce()+"','"+$KeywordRegistry.bulkMailRecipientStatus$hardBounce()+"') then MAIL_LOG.MAIL_LOGID else null end)", + SOFTBOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS ='"+$KeywordRegistry.bulkMailRecipientStatus$softBounce()+"' then MAIL_LOG.MAIL_LOGID else null end)", + HARDBOUNCECOUNT:"count (distinct case when MAIL_LOG.STATUS ='"+$KeywordRegistry.bulkMailRecipientStatus$hardBounce()+"' then MAIL_LOG.MAIL_LOGID else null end)", SENDCOUNT:"count (distinct MAIL_LOG.MAIL_LOGID)", RECEIVEDCOUNT:"count (distinct case when MAIL_LOG.STATUS='"+$KeywordRegistry.bulkMailRecipientStatus$sent()+"' then MAIL_LOG.MAIL_LOGID else null end)", UNSUBSCRIBECOUNT:"count (distinct case when WEBLINK_CLICK.WEBLINK_ID = '"+newSelect(["WEBLINK.WEBLINKID"]).from("WEBLINK").where("WEBLINK.PLACEHOLDER","rejectEmail").cell()+"' then WEBLINK_CLICK.MAIL_LOG_ID else null end)" diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js index 47d89a46ea5ec55ba9f3d56712e2e5c37be4bed6..a2edd886efa103efd448a1f78f0225491e92ef98 100644 --- a/process/Bulkmail_lib/process.js +++ b/process/Bulkmail_lib/process.js @@ -1,3 +1,5 @@ +import("CommunicationBlacklist_lib"); +import("EmailFilterHandling_lib"); import("system.logging"); import("system.entities"); import("MarketingCondition_lib"); @@ -97,6 +99,8 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) recipientData = entities.getRows(recipientLoadConfig); + var blacklist = new CommunicationBlacklist().loadBlacklistRecipients(pBulkMailId); + if (pIsTestRun) { testRecipientData = newSelect("BULKMAILTESTRECIPIENT.CONTACT_ID, BULKMAILTESTRECIPIENT.EMAIL_ADDRESS") @@ -152,6 +156,8 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) var successIds = []; var failedIds = []; + var bouncedSoftIds = []; + var bouncedHardIds = []; var sentDate = vars.get("$sys.date"); var mails = template.getReplacedEmailsByContactIds(contactIds, additionalPlaceholders); @@ -160,11 +166,15 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) var bulkMailLink = [["BulkMail", pBulkMailId]]; var activitySubject = translate.withArguments("Bulk mail \"%0\" sent", [bulkMailName]); + + var emailFilterProcessor = new IncomingEmailFilterProcessor().loadFilters(); + if (!pIsTestRun) { recipientData.forEach(function (recipient) { let isSuccess = false; + let bouncedStatus = null; let recipientId = recipient["BULKMAILRECIPIENTID"]; let contactId = recipient["CONTACT_ID"]; let emailAddress = recipient["EMAIL_ADDRESS"]; @@ -172,6 +182,7 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) let organisationId = recipient["ORGANISATION_ID"]; let email = mails[contactId]; let mailLogId = mailLogIds.get(contactId); + let recipientStatus = $KeywordRegistry.bulkMailRecipientStatus$sent(); if (email !== undefined && emailAddress) { email.toRecipients = [emailAddress]; @@ -180,6 +191,17 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) BulkMailUtils.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML()); isSuccess = email.send(); + if (!isSuccess) + { + var errorMessage = logging.toLogString(email.getMailError(), true); + var filterType = emailFilterProcessor.processError(errorMessage, contactId, emailAddress); + if (filterType == $KeywordRegistry.emailFilterType$bounceHard()) + bouncedStatus = $KeywordRegistry.bulkMailRecipientStatus$hardBounce(); + else if (filterType == $KeywordRegistry.emailFilterType$bounceSoft()) + bouncedStatus = $KeywordRegistry.bulkMailRecipientStatus$softBounce(); + + recipientStatus = bouncedStatus || $KeywordRegistry.bulkMailRecipientStatus$failed(); + } } //set the recipient status to 'sent' or 'failed' @@ -189,7 +211,7 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) "MAIL_LOGID": mailLogId, "MAIL_RUN_ID": mailrunId, "CONTACT_ID": contactId, - "STATUS": isSuccess ? $KeywordRegistry.bulkMailRecipientStatus$sent() : $KeywordRegistry.bulkMailRecipientStatus$failed(), + "STATUS": recipientStatus, "SENDER_EMAIL": emailSender, "RECIPIENT_EMAIL": emailAddress, "MAILING_SUBJECT": subjects[contactId], @@ -197,7 +219,22 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) }); //TODO: Klären was von alter Logik noch bleiben soll. Status macht nur Sinn wenn jede Bulkmail nur einmal gesendet wird. Bleiben Activitys? - Array.prototype.push.call(isSuccess ? successIds : failedIds, recipientId); + if (isSuccess) + { + sucessIds.push(recipientId); + } + else if (bouncedStatus == $KeywordRegistry.bulkMailRecipientStatus$softBounce()) + { + bouncedSoftIds.push(recipientId); + } + else if (bouncedStatus == $KeywordRegistry.bulkMailRecipientStatus$hardBounce()) + { + bouncedHardIds.push(recipientId); + } + else + { + failedIds.push(recipientId); + } if (isSuccess && createActivity == "1") { let activityData = { @@ -223,6 +260,18 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) "SENTDATE": sentDate }); + newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", bouncedSoftIds, SqlBuilder.IN()) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailRecipientStatus$softBounce(), + "SENTDATE": sentDate + }); + + newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", bouncedHardIds, SqlBuilder.IN()) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailRecipientStatus$hardBounce(), + "SENTDATE": sentDate + }); + newWhere("MAIL_RUN.MAIL_RUNID", mailrunId) .updateFields({ "STATUS": $KeywordRegistry.bulkMailStatus$sent(), diff --git a/process/CommunicationBlacklist_lib/process.js b/process/CommunicationBlacklist_lib/process.js index 238bf50b48bba7de016b8b7af07cfbe35367e94c..5c350a27f2fda0fd164a35c167cdf38b057d77ac 100644 --- a/process/CommunicationBlacklist_lib/process.js +++ b/process/CommunicationBlacklist_lib/process.js @@ -1,3 +1,5 @@ +import("KeywordRegistry_basic"); +import("system.entities"); import("Util_lib"); import("Sql_lib"); import("system.vars"); @@ -5,17 +7,18 @@ import("system.vars"); function CommunicationBlacklist () { - this.filter = null; + this.filter = CommunicationBlacklist.getMailRecipientBlacklistFilter(); + this.blacklistContactIds = new Set(); } /** * @return {CommunicationBlacklist} */ -CommunicationBlacklist.getMailRecipientBlacklist = function () +CommunicationBlacklist.getMailRecipientBlacklistFilter = function () { var filters = newSelect("FILTER") .from("EMAIL_FILTER_HANDLING") - .where("COMMUNICATIONBLACKLIST.BLACKLIST_TYPE", $KeywordRegistry.communicationBlacklistType$emailRecipientFilter()) + .where("EMAIL_FILTER_HANDLING.FILTER_TYPE", $KeywordRegistry.emailFilterType$blacklist()) .table(); var filterMappingFn = function ([blacklistFilter]) @@ -27,19 +30,34 @@ CommunicationBlacklist.getMailRecipientBlacklist = function () return blacklistFilter.filter; }; - var blacklist = new CommunicationBlacklist(); - blacklist.filter = { + return { type: "group", operator: "AND", childs: filters.map(filterMappingFn).filter(Utils.isObject) }; - return blacklist; } -CommunicationBlacklist.prototype.getCondition = function () +CommunicationBlacklist.prototype.getFilter = function () { - return new FilterSqlTranslator() - .filter(this.filter) - .table("BULKMAILRECIPIENT") - .getSqlCondition(); -} \ No newline at end of file + return this.filter; +} + +CommunicationBlacklist.prototype.loadBlacklistRecipients = function (pBulkMailId) +{ + var blacklistLoadConfig = entities.createConfigForLoadingRows() + .fields(["CONTACT_ID"]) + .entity("BulkmailRecipient_entity") + .provider("RecipientsToBeMailed") + .addParameter("BulkMailId_param", pBulkMailId) + .filter(JSON.stringify(this.filter)); + var blacklist = entities.getRows(blacklistLoadConfig); + blacklist.forEach(function (recipient) + { + this.blacklistContactIds.add(recipient["CONTACT_ID"]); + }, this); +} + +CommunicationBlacklist.prototype.hasContactId = function (pContactId) +{ + return this.blacklistContactIds.has(pContactId); +} diff --git a/process/EmailFilterHandling_lib/process.js b/process/EmailFilterHandling_lib/process.js index 15db9b516dc61fe98c6918b331de7ae2b361d875..1ef6f1da890bbd522858995737735672bb8aec1f 100644 --- a/process/EmailFilterHandling_lib/process.js +++ b/process/EmailFilterHandling_lib/process.js @@ -18,8 +18,8 @@ IncomingEmailFilterProcessor.prototype.loadFilters = function () var bounceFilters = newSelect(["TITLE", "FILTER_TYPE", "FILTER", "WORKFLOWDEFINITION_KEY"]) .from("EMAIL_FILTER_HANDLING") .where("EMAIL_FILTER_HANDLING.ISACTIVE", "1") - .and(newWhere("EMAIL_FILTER.FILTER_TYPE", $KeywordRegistry.emailFilterType$bounceSoft()) - .or("EMAIL_FILTER.FILTER_TYPE", $KeywordRegistry.emailFilterType$bounceHard())) + .and(newWhere("EMAIL_FILTER_HANDLING.FILTER_TYPE", $KeywordRegistry.emailFilterType$bounceSoft()) + .or("EMAIL_FILTER_HANDLING.FILTER_TYPE", $KeywordRegistry.emailFilterType$bounceHard())) .orderBy("PRIORITY asc") .table(); @@ -27,6 +27,7 @@ IncomingEmailFilterProcessor.prototype.loadFilters = function () { return new IncomingEmailFilter(title, type, filterJson, workflowKey); }); + return this; } /** @@ -34,13 +35,30 @@ IncomingEmailFilterProcessor.prototype.loadFilters = function () */ IncomingEmailFilterProcessor.prototype.process = function (pEmail) { + var bouncedStatus = null; this.emailFilters.forEach(function (emailFilter) { if (emailFilter.checkEmail(pEmail)) { emailFilter.startWorkflow(); + bouncedStatus = emailFilter.type; } }); + return bouncedStatus; +} + +IncomingEmailFilterProcessor.prototype.processError = function (pErrorMessage, pContactId, pEmailAddress) +{ + var bouncedStatus = null; + this.emailFilters.forEach(function (emailFilter) + { + if (emailFilter.checkEmailError(pErrorMessage)) + { + emailFilter.startWorkflow(pContactId, pEmailAddress); + bouncedStatus = emailFilter.type; + } + }); + return bouncedStatus; } /** @@ -52,7 +70,7 @@ function IncomingEmailFilter (pTitle, pType, pFilterJson, pWorkflowKey) this.type = pType; this.filter = new JditoFilter() .filterJSON(pFilterJson) - .fieldOrder(["SENDER", "SUBJECT", "BODY_PLAIN", "BODY_REGEX", "ATTACHMENTCOUNT"]) + .fieldOrder(["SENDER", "SUBJECT", "BODY_PLAIN", "BODY_REGEX", "ATTACHMENTCOUNT", "SENDERROR"]) .addSpecialCheckFn("BODY_REGEX", function (rowValue, filterValue, operator) { var regExParts = filterValue.match(new RegExp('^/(.*?)/([gimy]*)$')); @@ -64,7 +82,7 @@ function IncomingEmailFilter (pTitle, pType, pFilterJson, pWorkflowKey) return regEx.test(rowValue); }, this); this.workflowKey = pWorkflowKey; - this.isFallthrough = pIsFallthrough; + this.isFallthrough = false; } /** @@ -79,11 +97,17 @@ IncomingEmailFilter.prototype.checkEmail = function (pEmail) var subject = pEmail[mail.MAIL_SUBJECT]; var body = pEmail[mail.MAIL_TEXT]; var attachmentCount = pEmail[mail.MAIL_ATTACHMENTCOUNT]; - var emailRecord = [sender, subject, body, body, attachmentCount]; + var emailRecord = [sender, subject, body, body, attachmentCount, ""]; return this.filter.checkRecord(emailRecord); } +IncomingEmailFilter.prototype.checkEmailError = function (pError) +{ + var emailRecord = ["", "", "", "", "", pError]; + return this.filter.checkRecord(emailRecord); +} + /** * Inserts an entry into the MAIL_LOG table to protocol the received email. * @@ -94,18 +118,22 @@ IncomingEmailFilter.prototype.writeMailLog = function (pEmail) { new SqlBuilder.insertFields({ "MAIL_LOGID": util.getNewUUID() + }, pTableName); } /** * Starts the workflow that has been defined for the email filter. */ -IncomingEmailFilter.prototype.startWorkflow = function () +IncomingEmailFilter.prototype.startWorkflow = function (pContactId, pEmailAddress) { if (this.workflowKey) { workflow.startProcessByKey(this.workflowKey, { - + contactId: pContactId, + emailAddress: pEmailAddress, + channelType: $KeywordRegistry.communicationChannelType$communication(), + medium: $KeywordRegistry.communicationMediumCampaign$mail() }); } } diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js index 1c2acdb21f9f2c28b2b1f9d2dae3c643d69d4538..68f07458d6610719aa259d47419ef72169fe63fb 100644 --- a/process/Email_lib/process.js +++ b/process/Email_lib/process.js @@ -169,6 +169,7 @@ function Email(pBody) this.bccRecipients = []; this.attachmentTemplates = []; this.emlFile = null; + this._error = null; //caches the error if sending fails } /** @@ -409,12 +410,24 @@ Email.prototype.send = function (pUser) question.showMessage(translate.withArguments("Mailbridge failed: user '%0' is unknown, contact an administrator.", [mailbridgeTitle]), question.ERROR, translate.text("Error")); } // remove from cache - mail.deleteMail(mailId) + mail.deleteMail(mailId); + this._error = null; return sentMails > 0; } catch (ex) { + this._error = ex; logging.log(ex); return false; } +} + +/** + * Returns the error from sending the email + * + * @return {Error} error if sending failed, or null if it was sucessful + */ +Email.prototype.getMailError = function () +{ + return this._error; } \ No newline at end of file diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index 5fc995435666ed19b5a5eb716a1b02db218bdd7b..86bfc662594e207c5911ec0f786cde055b67d34d 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -237,6 +237,8 @@ $KeywordRegistry.bulkMailRecipientStatus = function(){return "BulkMailRecipientS $KeywordRegistry.bulkMailRecipientStatus$pending = function(){return "EMAILPENDING";}; $KeywordRegistry.bulkMailRecipientStatus$sent = function(){return "EMAILSENT";}; $KeywordRegistry.bulkMailRecipientStatus$failed = function(){return "EMAILFAILED";}; +$KeywordRegistry.bulkMailRecipientStatus$softBounce = function(){return "EMAILBOUNCED_SOFT";}; +$KeywordRegistry.bulkMailRecipientStatus$hardBounce = function(){return "EMAILBOUNCED_HARD";}; $KeywordRegistry.bulkMailStatus = function(){return "BulkMailStatus";}; $KeywordRegistry.bulkMailStatus$notSent = function(){return "BULKMAILNOTSENT";}; @@ -426,5 +428,5 @@ $KeywordRegistry.advertisingDelivery$mail = function(){return "ADVERTDELIVERYMAI $KeywordRegistry.emailFilterType = function(){return "EmailFilterType";}; $KeywordRegistry.emailFilterType$blacklist = function(){return "EMAIL_FILTER_BLACKLIST";}; -$KeywordRegistry.emailFilterType$bounceSoft = function(){return "EMAIL_FILTER_BOUNCEHARD";}; -$KeywordRegistry.emailFilterType$bounceHard = function(){return "EMAIL_FILTER_BOUNCESOFT";}; \ No newline at end of file +$KeywordRegistry.emailFilterType$bounceSoft = function(){return "EMAIL_FILTER_BOUNCESOFT";}; +$KeywordRegistry.emailFilterType$bounceHard = function(){return "EMAIL_FILTER_BOUNCEHARD";}; \ No newline at end of file diff --git a/process/mailbridge/process.js b/process/mailbridge/process.js index 88f17d8b32509fd74629bb49748cf66968984268..3d5144d29fdd14251a0918d11ea3b900a800a823 100644 --- a/process/mailbridge/process.js +++ b/process/mailbridge/process.js @@ -1,4 +1,5 @@ import("IncomingEmailExecutor_lib"); +import("EmailFilterHandling_lib"); import("system.text"); import("system.vars"); import("system.mail"); @@ -9,4 +10,5 @@ var recipients = text.decodeMS(vars.getString("$local.recipients")); var mailObj = mail.resolveMail(vars.getString("$local.mail")); var incomingMailExec = new IncomingEmailExecutor(mailObj); +incomingMailExec.attachProcessor(new IncomingEmailFilterProcessor().loadFilters()); incomingMailExec.autoProcess(); \ No newline at end of file