diff --git a/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/changelog.xml b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/changelog.xml new file mode 100644 index 0000000000000000000000000000000000000000..de169ea5bfc61c615c96663b65dea30519245fff --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/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="create_communicationBlacklist.xml"/> + <include relativeToChangelogFile="true" file="insert_blacklistTypeKeyword.xml"/> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/create_communicationBlacklist.xml b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/create_communicationBlacklist.xml new file mode 100644 index 0000000000000000000000000000000000000000..faf00d8192c66f4d93f76288fd73a64243d37807 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/create_communicationBlacklist.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="9415eb4f-ea93-433c-8a69-1bdb77c6ec87"> + <createTable tableName="COMMUNICATIONBLACKLIST"> + <column name="COMMUNICATIONBLACKLISTID" type="CHAR(36)"> + <constraints nullable="false" primaryKey="true" primaryKeyName="PK_COMMUNICATIONBLACKLISTID"/> + </column> + <column name="BLACKLIST_TYPE" type="VARCHAR(36)"> + <constraints nullable="false"/> + </column> + <column name="BLACKLIST_FILTER" type="NCLOB"/> + <column name="REASON" type="NVARCHAR(500)"/> + <column name="START_DATE" type="DATETIME"/> + <column name="END_DATE" type="DATETIME"/> + <column name="DATE_NEW" type="DATETIME"> + <constraints nullable="false"/> + </column> + <column name="USER_NEW" type="VARCHAR(50)"> + <constraints nullable="false"/> + </column> + <column name="DATE_EDIT" type="DATETIME"/> + <column name="USER_EDIT" type="VARCHAR(50)"/> + </createTable> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/insert_blacklistTypeKeyword.xml b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/insert_blacklistTypeKeyword.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2a6c8057c98bad7b41e2298ed807dbb4032dfc7 --- /dev/null +++ b/.liquibase/Data_alias/basic/2021.0.2/CommunicationBlacklist/insert_blacklistTypeKeyword.xml @@ -0,0 +1,23 @@ +<?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="89ba3828-288c-4af7-bf98-feccc01ce312"> + <insert tableName="AB_KEYWORD_CATEGORY"> + <column name="AB_KEYWORD_CATEGORYID" value="e980999c-0f8e-484b-852b-92d60c38c14f"/> + <column name="NAME" value="CommunicationBlacklistType"/> + <column name="SORTINGBY" valueNumeric="0"/> + <column name="SORTINGDIRECTION" value="ASC"/> + </insert> + <insert tableName="AB_KEYWORD_ENTRY"> + <column name="AB_KEYWORD_ENTRYID" value="a1c8d2c6-54c7-4e9d-9792-dd576ac6f43e"/> + <column name="AB_KEYWORD_CATEGORY_ID" value="e980999c-0f8e-484b-852b-92d60c38c14f"/> + <column name="KEYID" value="BLACKLIST_TYPE_EMAILRECIPIENT_FILTER"/> + <column name="TITLE" value="Recipient filter"/> + <column name="CONTAINER" value="CommunicationBlacklistType"/> + <column name="SORTING" valueNumeric="1"/> + <column name="ISACTIVE" valueNumeric="1"/> + <column name="ISESSENTIAL" valueNumeric="0"/> + </insert> + </changeSet> +</databaseChangeLog> \ No newline at end of file diff --git a/.liquibase/Data_alias/basic/2021.0.2/changelog.xml b/.liquibase/Data_alias/basic/2021.0.2/changelog.xml index 53aa034099c548707609dbcbe2b635b4f0f9352d..f73015f9608794f9dafe98ab79e7e6ee741baca2 100644 --- a/.liquibase/Data_alias/basic/2021.0.2/changelog.xml +++ b/.liquibase/Data_alias/basic/2021.0.2/changelog.xml @@ -7,4 +7,5 @@ <include relativeToChangelogFile="true" file="CommunicationSettings/changelog.xml"/> <include relativeToChangelogFile="true" file="change_SalesprojectMemberRole.xml"/> <include relativeToChangelogFile="true" file="Interest/changelog.xml"/> + <include relativeToChangelogFile="true" file="CommunicationBlacklist/changelog.xml"/> </databaseChangeLog> \ No newline at end of file diff --git a/aliasDefinition/Data_alias/Data_alias.aod b/aliasDefinition/Data_alias/Data_alias.aod index 70a35bfc8c893af56d49c4a09324e20f26533614..d2efab6fb2dcbdc8ad9b15821a721d085f9ad094 100644 --- a/aliasDefinition/Data_alias/Data_alias.aod +++ b/aliasDefinition/Data_alias/Data_alias.aod @@ -19729,6 +19729,166 @@ </entityFieldDb> </entityFields> </entityDb> + <entityDb> + <name>COMMUNICATIONBLACKLIST</name> + <dbName></dbName> + <idColumn>COMMUNICATIONBLACKLISTID</idColumn> + <idGeneratorType v="0" /> + <idGeneratorInterval v="1" /> + <documentation></documentation> + <title></title> + <description></description> + <auditSyncConfig> + <name>auditSyncConfig</name> + <auditMode v="0" /> + <syncActive v="false" /> + <syncComplete v="true" /> + <syncDirection v="1" /> + <syncIds></syncIds> + </auditSyncConfig> + <entityFields> + <entityFieldDb> + <name>BLACKLIST_FILTER</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="-1" /> + <size v="2147483647" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>DATE_EDIT</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="93" /> + <size v="19" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>END_DATE</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="93" /> + <size v="19" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>START_DATE</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="93" /> + <size v="19" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>DATE_NEW</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="93" /> + <size v="19" /> + <scale v="0" /> + <notNull v="true" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>USER_NEW</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="12" /> + <size v="50" /> + <scale v="0" /> + <notNull v="true" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>COMMUNICATIONBLACKLISTID</name> + <dbName></dbName> + <primaryKey v="true" /> + <columnType v="1" /> + <size v="36" /> + <scale v="0" /> + <notNull v="true" /> + <isUnique v="true" /> + <index v="true" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>REASON</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="12" /> + <size v="500" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>USER_EDIT</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="12" /> + <size v="50" /> + <scale v="0" /> + <notNull v="false" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + <entityFieldDb> + <name>BLACKLIST_TYPE</name> + <dbName></dbName> + <primaryKey v="false" /> + <columnType v="12" /> + <size v="36" /> + <scale v="0" /> + <notNull v="true" /> + <isUnique v="false" /> + <index v="false" /> + <documentation></documentation> + <title></title> + <description></description> + </entityFieldDb> + </entityFields> + </entityDb> </entities> </entityGroup> </aliasDefDb> diff --git a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod index 7af92c53743f3adb952f2f0fa986a937f9183ccd..5f7436d899887b2367b9f90bf001c6fd9ba924cb 100644 --- a/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod +++ b/application/_____SYSTEM_APPLICATION_NEON/_____SYSTEM_APPLICATION_NEON.aod @@ -200,6 +200,10 @@ <name>Interest</name> <kind v="10077" /> </entityNode> + <entityNode> + <name>CommunicationBlacklist</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 5cd67de037d75128d0b09319209d18296dc70b30..e799fd72fcc3c4068ba9f766f62feb22959da90c 100644 --- a/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod +++ b/entity/BulkMailRecipient_entity/BulkMailRecipient_entity.aod @@ -82,7 +82,6 @@ <entityField> <name>EMAIL_ADDRESS</name> <title>Email</title> - <consumer>EmailAdresses</consumer> <textInputAllowed v="true" /> <valueProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/email_address/valueProcess.js</valueProcess> <displayValueProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/email_address/displayValueProcess.js</displayValueProcess> @@ -108,7 +107,7 @@ <state>READONLY</state> </entityField> <entityField> - <name>HASCOMMRESTRICTION</name> + <name>HASCOMMUNICATIONREJECTION</name> <title>Advertising ban</title> </entityField> <entityActionGroup> @@ -176,6 +175,36 @@ </entityActionField> </children> </entityActionGroup> + <entityParameter> + <name>ExcludeCommunicationRejecting_param</name> + <expose v="true" /> + </entityParameter> + <entityParameter> + <name>ExcludedStatus_param</name> + <expose v="true" /> + </entityParameter> + <entityProvider> + <name>RecipientsToBeMailed</name> + <children> + <entityParameter> + <name>ExcludedStatus_param</name> + <valueProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludedstatus_param/valueProcess.js</valueProcess> + <expose v="false" /> + </entityParameter> + <entityParameter> + <name>ExcludeCommunicationRejecting_param</name> + <valueProcess>%aditoprj%/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludecommunicationrejecting_param/valueProcess.js</valueProcess> + <expose v="false" /> + </entityParameter> + </children> + </entityProvider> + <entityParameter> + <name>IsTestMail_param</name> + <expose v="true" /> + </entityParameter> + <entityParameter> + <name>ExcludeBlacklisted_param</name> + </entityParameter> </entityFields> <recordContainers> <dbRecordContainer> @@ -232,13 +261,10 @@ <name>CONTACT_ID.displayValue</name> <expression>%aditoprj%/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/contact_id.displayvalue/expression.js</expression> </dbRecordFieldMapping> - <dbRecordFieldMapping> - <name>HASCOMMRESTRICTION.value</name> - <expression>%aditoprj%/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommrestriction.value/expression.js</expression> - </dbRecordFieldMapping> <dbRecordFieldMapping> <name>EMAIL_ADDRESS.value</name> <recordfield>BULKMAILRECIPIENT.EMAIL_ADDRESS</recordfield> + <isFilterable v="true" /> </dbRecordFieldMapping> <dbRecordFieldMapping> <name>PROBLEM.value</name> @@ -252,6 +278,10 @@ <name>TEST_RECIPIENT.value</name> <recordfield>BULKMAILRECIPIENT.TEST_RECIPIENT</recordfield> </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>HASCOMMUNICATIONREJECTION.value</name> + <expression>%aditoprj%/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommunicationrejection.value/expression.js</expression> + </dbRecordFieldMapping> </recordFieldMappings> <linkInformation> <linkInformation> diff --git a/entity/BulkMailRecipient_entity/entityfields/icon/colorProcess.js b/entity/BulkMailRecipient_entity/entityfields/icon/colorProcess.js index f4a5d6a797f85b33b0d1d819d44e0015a28d24c9..32b81b7db33e5508f206cc74db74609c9adae950 100644 --- a/entity/BulkMailRecipient_entity/entityfields/icon/colorProcess.js +++ b/entity/BulkMailRecipient_entity/entityfields/icon/colorProcess.js @@ -2,5 +2,5 @@ import("system.vars"); import("system.result"); import("system.neon"); -if (vars.get("$field.HASCOMMRESTRICTION") == "true" || !vars.get("$field.EMAIL_ADDRESS")) +if (vars.get("$field.HASCOMMUNICATIONREJECTION") == "true" || !vars.get("$field.EMAIL_ADDRESS")) result.string(neon.PRIORITY_HIGH_COLOR); \ No newline at end of file diff --git a/entity/BulkMailRecipient_entity/entityfields/icon/valueProcess.js b/entity/BulkMailRecipient_entity/entityfields/icon/valueProcess.js index c67e7c5712da325df0f23f47eee957ce28a3ed54..711c154ccbca6c140fd523e0d296c08b66fb094f 100644 --- a/entity/BulkMailRecipient_entity/entityfields/icon/valueProcess.js +++ b/entity/BulkMailRecipient_entity/entityfields/icon/valueProcess.js @@ -4,7 +4,7 @@ import("Contact_lib"); var type = ContactUtils.getContactTypeByPersOrg(vars.get("$field.PERSON_ID"), vars.get("$field.ORGANISATION_ID")); var icon; -if (vars.get("$field.HASCOMMRESTRICTION") == "true") +if (vars.get("$field.HASCOMMUNICATIONREJECTION") == "true") icon = "VAADIN:BAN"; else if (type == 1) icon = "VAADIN:BUILDING"; diff --git a/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludecommunicationrejecting_param/valueProcess.js b/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludecommunicationrejecting_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..40effa0178464da0c7850912345f19c7fa95975a --- /dev/null +++ b/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludecommunicationrejecting_param/valueProcess.js @@ -0,0 +1,3 @@ +import("system.result"); + +result.string(true); \ No newline at end of file diff --git a/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludedstatus_param/valueProcess.js b/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludedstatus_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..ae0c566f408d9f9a4bd2181699b7fadeab9a02a4 --- /dev/null +++ b/entity/BulkMailRecipient_entity/entityfields/recipientstobemailed/children/excludedstatus_param/valueProcess.js @@ -0,0 +1,6 @@ +import("KeywordRegistry_basic"); +import("system.result"); + +result.string(JSON.stringify([ + $KeywordRegistry.bulkMailRecipientStatus$sent() +])); \ No newline at end of file diff --git a/entity/BulkMailRecipient_entity/recordcontainers/db/conditionProcess.js b/entity/BulkMailRecipient_entity/recordcontainers/db/conditionProcess.js index 01d4a5dc9c76ee5f350521eea0f4a2bb6016a3ec..7e2155661d1e08884d88ba7c0fe4c970c3cab458 100644 --- a/entity/BulkMailRecipient_entity/recordcontainers/db/conditionProcess.js +++ b/entity/BulkMailRecipient_entity/recordcontainers/db/conditionProcess.js @@ -1,5 +1,38 @@ +import("KeywordRegistry_basic"); +import("system.vars"); +import("Util_lib"); import("system.db"); import("system.result"); import("Sql_lib"); +import("MarketingCondition_lib"); -result.string(newWhere("BULKMAILRECIPIENT.BULKMAIL_ID", "$param.BulkMailId_param").toString()); \ No newline at end of file +var excludeWithCommunicationRejection = Utils.toBoolean(vars.get("$param.ExcludeCommunicationRejecting_param")); +var excludeBlacklisted = Utils.toBoolean(vars.get("$param.ExcludeBlacklisted_param")); +var excludedStatus = Utils.parseJSON(vars.get("$param.ExcludedStatus_param")); +var isTestMail = Utils.toBoolean(vars.get("$param.IsTestMail_param")); + +var condition = newWhere("BULKMAILRECIPIENT.BULKMAIL_ID", "$param.BulkMailId_param"); +if (isTestMail) +{ + condition.and("BULKMAILRECIPIENT.TEST_RECIPIENT", 1); +} +else +{ + if (excludeWithCommunicationRejection) + { + condition.and(new CommunicationSettingsCondition() + .medium($KeywordRegistry.communicationMediumCampaign$mail(), "BULKMAILRECIPIENT.EMAIL_ADDRESS") + .rejected() + .buildNotExistsCondition()); + } + if (excludeBlacklisted) + { + condition.and("not (" + CommunicationBlacklist.getMailRecipientBlacklist().buildCondition() + ")"); + } + if (!Utils.isNullOrEmpty(excludedStatus)) + { + condition.and("BULKMAILRECIPIENT.STATUS", excludedStatus, SqlBuilder.NOT_IN()); + } +} + +result.string(condition.toSource()); \ No newline at end of file diff --git a/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommrestriction.value/expression.js b/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommrestriction.value/expression.js deleted file mode 100644 index 04cd6d4dc91c5a7e36cd82b78301acf456fc05ff..0000000000000000000000000000000000000000 --- a/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommrestriction.value/expression.js +++ /dev/null @@ -1,9 +0,0 @@ -import("Sql_lib"); -import("KeywordRegistry_basic"); -import("Contact_lib"); -import("system.db"); -import("system.result"); - -var commRestrictionCond = ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail()); -var sql = SqlBuilder.caseWhen(commRestrictionCond).thenString("true").elseString("false"); -result.string(sql.toString()); diff --git a/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommunicationrejection.value/expression.js b/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommunicationrejection.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..cd8b6dfbc38990c87c8b99aff9833f3730a71c6f --- /dev/null +++ b/entity/BulkMailRecipient_entity/recordcontainers/db/recordfieldmappings/hascommunicationrejection.value/expression.js @@ -0,0 +1,25 @@ +import("system.vars"); +import("Util_lib"); +import("MarketingCondition_lib"); +import("Sql_lib"); +import("KeywordRegistry_basic"); +import("Contact_lib"); +import("system.db"); +import("system.result"); + +var sql; +//if recipients with communication restriction are excluded by the where-condition, this can never be true +if (Utils.toBoolean(vars.get("$param.ExcludeCommunicationRejecting_param"))) +{ + sql = "'false'"; +} +else +{ + var communicationSettingsCondition = new CommunicationSettingsCondition() + .medium($KeywordRegistry.communicationMediumCampaign$mail(), "BULKMAILRECIPIENT.EMAIL_ADDRESS") + .rejected() + .buildNotExistsCondition(); + sql = SqlBuilder.caseWhen(communicationSettingsCondition).thenString("true").elseString("false").toString(); +} + +result.string(sql); diff --git a/entity/CommunicationBlacklist_entity/CommunicationBlacklist_entity.aod b/entity/CommunicationBlacklist_entity/CommunicationBlacklist_entity.aod new file mode 100644 index 0000000000000000000000000000000000000000..58cbbc2265ecbeff8ff3fb8e80e2c5a44d4b5866 --- /dev/null +++ b/entity/CommunicationBlacklist_entity/CommunicationBlacklist_entity.aod @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.18" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.18"> + <name>CommunicationBlacklist_entity</name> + <title>Blacklist</title> + <majorModelMode>DISTRIBUTED</majorModelMode> + <onValidation>%aditoprj%/entity/CommunicationBlacklist_entity/onValidation.js</onValidation> + <iconId>VAADIN:BAN</iconId> + <recordContainer>db</recordContainer> + <entityFields> + <entityProvider> + <name>#PROVIDER</name> + </entityProvider> + <entityProvider> + <name>#PROVIDER_AGGREGATES</name> + <useAggregates v="true" /> + </entityProvider> + <entityField> + <name>COMMUNICATIONBLACKLISTID</name> + </entityField> + <entityField> + <name>START_DATE</name> + <title>Start date</title> + <contentType>DATE</contentType> + <resolution>DAY</resolution> + </entityField> + <entityField> + <name>END_DATE</name> + <title>End date</title> + <contentType>DATE</contentType> + <resolution>DAY</resolution> + </entityField> + <entityField> + <name>BLACKLIST_FILTER</name> + <title>Condition</title> + <contentType>FILTER_TREE</contentType> + <stateProcess>%aditoprj%/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/stateProcess.js</stateProcess> + <valueProcess>%aditoprj%/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/valueProcess.js</valueProcess> + </entityField> + <entityField> + <name>REASON</name> + <title>Reason</title> + <contentType>LONG_TEXT</contentType> + <mandatory v="true" /> + </entityField> + <entityField> + <name>BLACKLIST_TYPE</name> + <title>Type</title> + <consumer>BlacklistTypeKeyword</consumer> + <mandatory v="true" /> + </entityField> + <entityConsumer> + <name>BlacklistTypeKeyword</name> + <dependency> + <name>dependency</name> + <entityName>KeywordEntry_entity</entityName> + <fieldName>SpecificContainerKeywords</fieldName> + </dependency> + <children> + <entityParameter> + <name>ContainerName_param</name> + <valueProcess>%aditoprj%/entity/CommunicationBlacklist_entity/entityfields/blacklisttypekeyword/children/containername_param/valueProcess.js</valueProcess> + </entityParameter> + </children> + </entityConsumer> + </entityFields> + <recordContainers> + <dbRecordContainer> + <name>db</name> + <alias>Data_alias</alias> + <recordFieldMappings> + <dbRecordFieldMapping> + <name>BLACKLIST_FILTER.value</name> + <recordfield>COMMUNICATIONBLACKLIST.BLACKLIST_FILTER</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>BLACKLIST_TYPE.value</name> + <recordfield>COMMUNICATIONBLACKLIST.BLACKLIST_TYPE</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>COMMUNICATIONBLACKLISTID.value</name> + <recordfield>COMMUNICATIONBLACKLIST.COMMUNICATIONBLACKLISTID</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>END_DATE.value</name> + <recordfield>COMMUNICATIONBLACKLIST.END_DATE</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>REASON.value</name> + <recordfield>COMMUNICATIONBLACKLIST.REASON</recordfield> + </dbRecordFieldMapping> + <dbRecordFieldMapping> + <name>START_DATE.value</name> + <recordfield>COMMUNICATIONBLACKLIST.START_DATE</recordfield> + <isFilterable v="true" /> + </dbRecordFieldMapping> + </recordFieldMappings> + <linkInformation> + <linkInformation> + <name>007ad42a-0a7a-49a9-a71d-917ee3c94aa7</name> + <tableName>COMMUNICATIONBLACKLIST</tableName> + <primaryKey>COMMUNICATIONBLACKLISTID</primaryKey> + <isUIDTable v="true" /> + <readonly v="false" /> + </linkInformation> + </linkInformation> + </dbRecordContainer> + </recordContainers> +</entity> diff --git a/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/stateProcess.js b/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/stateProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..234a26e9827c511b12b09fbbe1f5552bfd78f912 --- /dev/null +++ b/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/stateProcess.js @@ -0,0 +1,12 @@ +import("system.result"); +import("KeywordRegistry_basic"); +import("system.vars"); +import("system.neon"); + +var state = neon.COMPONENTSTATE_INVISIBLE; +if (vars.get("$field.BLACKLIST_TYPE") == $KeywordRegistry.communicationBlacklistType$emailRecipientFilter()) +{ + state = neon.COMPONENTSTATE_EDITABLE; +} + +result.string(state); \ No newline at end of file diff --git a/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/valueProcess.js b/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..0bb45e823a21888b77385d2950bfb48fc5d530d9 --- /dev/null +++ b/entity/CommunicationBlacklist_entity/entityfields/blacklist_filter/valueProcess.js @@ -0,0 +1,9 @@ +import("system.neon"); +import("system.vars"); +import("system.result"); + +if (!vars.get("$this.value") && (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)) +{ + var emptyFilter = {entity: "BulkMailRecipient_entity", filter: {type: "group", operator: "AND", childs: []}}; + result.string(JSON.stringify(emptyFilter)); +} \ No newline at end of file diff --git a/entity/CommunicationBlacklist_entity/entityfields/blacklisttypekeyword/children/containername_param/valueProcess.js b/entity/CommunicationBlacklist_entity/entityfields/blacklisttypekeyword/children/containername_param/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..1e28921b3c32d79d55a29aebe44878a9c766c91d --- /dev/null +++ b/entity/CommunicationBlacklist_entity/entityfields/blacklisttypekeyword/children/containername_param/valueProcess.js @@ -0,0 +1,4 @@ +import("KeywordRegistry_basic"); +import("system.result"); + +result.string($KeywordRegistry.communicationBlacklistType()); \ No newline at end of file diff --git a/entity/CommunicationBlacklist_entity/onValidation.js b/entity/CommunicationBlacklist_entity/onValidation.js new file mode 100644 index 0000000000000000000000000000000000000000..0652d9c0f9ff0ba032e0b7561c04ee345ba99223 --- /dev/null +++ b/entity/CommunicationBlacklist_entity/onValidation.js @@ -0,0 +1,15 @@ +import("system.translate"); +import("Util_lib"); +import("system.result"); +import("KeywordRegistry_basic"); +import("system.vars"); +import("system.neon"); +import("JditoFilter_lib"); + +if (vars.get("$field.BLACKLIST_TYPE") == $KeywordRegistry.communicationBlacklistType$emailRecipientFilter()) +{ + var filter = Utils.parseJSON(vars.get("$field.BLACKLIST_FILTER")); + filter = new FilterConditionGroup(filter); + if (filter.isEmpty()) + result.string(translate.text("Filter can't be empty")); +} \ No newline at end of file diff --git a/entity/KeywordEntry_entity/KeywordEntry_entity.aod b/entity/KeywordEntry_entity/KeywordEntry_entity.aod index c2d0201747e72019fda806a3f8407b090ee0d97e..d7ba8e60fc2cf43e6a449d999124287d7e15de23 100644 --- a/entity/KeywordEntry_entity/KeywordEntry_entity.aod +++ b/entity/KeywordEntry_entity/KeywordEntry_entity.aod @@ -684,6 +684,12 @@ <fieldName>StatusKeyword</fieldName> <isConsumer v="false" /> </entityDependency> + <entityDependency> + <name>a4d04777-82dc-4384-a4a5-c6a4a71e7a65</name> + <entityName>CommunicationBlacklist_entity</entityName> + <fieldName>BlacklistTypeKeyword</fieldName> + <isConsumer v="false" /> + </entityDependency> </dependencies> <children> <entityParameter> diff --git a/neonContext/CommunicationBlacklist/CommunicationBlacklist.aod b/neonContext/CommunicationBlacklist/CommunicationBlacklist.aod index e6c6084fa90164ca1d5628feb39af6a61629c164..9e69819413f07b0c7b52eb16983d9409f9226edf 100644 --- a/neonContext/CommunicationBlacklist/CommunicationBlacklist.aod +++ b/neonContext/CommunicationBlacklist/CommunicationBlacklist.aod @@ -1,5 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> <neonContext 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/neonContext/1.1.1"> <name>CommunicationBlacklist</name> + <title>Blacklist</title> <majorModelMode>DISTRIBUTED</majorModelMode> + <filterView>CommunicationBlacklistFilter_view</filterView> + <editView>CommunicationBlacklistEdit_view</editView> + <entity>CommunicationBlacklist_entity</entity> + <references> + <neonViewReference> + <name>664796b3-d90b-439d-b960-a1f09e00c99d</name> + <view>CommunicationBlacklistFilter_view</view> + </neonViewReference> + <neonViewReference> + <name>c08fe896-0181-4243-8639-cb96e302d3c8</name> + <view>CommunicationBlacklistEdit_view</view> + </neonViewReference> + </references> </neonContext> diff --git a/neonView/CommunicationBlacklistEdit_view/CommunicationBlacklistEdit_view.aod b/neonView/CommunicationBlacklistEdit_view/CommunicationBlacklistEdit_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..7ca289f4bd074cc4bb8c460be628c7e0492d683b --- /dev/null +++ b/neonView/CommunicationBlacklistEdit_view/CommunicationBlacklistEdit_view.aod @@ -0,0 +1,39 @@ +<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> + <name>CommunicationBlacklistEdit_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <size>SMALL</size> + <layout> + <boxLayout> + <name>layout</name> + </boxLayout> + </layout> + <children> + <genericViewTemplate> + <name>Generic</name> + <editMode v="true" /> + <fields> + <entityFieldLink> + <name>5bf6cb8b-20da-4e64-ab86-e654e1f9f6a5</name> + <entityField>BLACKLIST_TYPE</entityField> + </entityFieldLink> + <entityFieldLink> + <name>bad5d056-972e-4d10-b3c7-a25bf6233cb5</name> + <entityField>BLACKLIST_FILTER</entityField> + </entityFieldLink> + <entityFieldLink> + <name>f74c2a6a-1b70-4914-8059-8866a0e5db77</name> + <entityField>START_DATE</entityField> + </entityFieldLink> + <entityFieldLink> + <name>2b2bb0e7-7cdf-42ca-8c0b-89d1d869fa44</name> + <entityField>END_DATE</entityField> + </entityFieldLink> + <entityFieldLink> + <name>947da750-50bd-47bb-af2d-7446b3756482</name> + <entityField>REASON</entityField> + </entityFieldLink> + </fields> + </genericViewTemplate> + </children> +</neonView> diff --git a/neonView/CommunicationBlacklistFilter_view/CommunicationBlacklistFilter_view.aod b/neonView/CommunicationBlacklistFilter_view/CommunicationBlacklistFilter_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..232b308af1e76c7e8cfb74d7ba3b0c25fa595806 --- /dev/null +++ b/neonView/CommunicationBlacklistFilter_view/CommunicationBlacklistFilter_view.aod @@ -0,0 +1,34 @@ +<?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.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8"> + <name>CommunicationBlacklistFilter_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <filterable v="true" /> + <layout> + <groupLayout> + <name>layout</name> + </groupLayout> + </layout> + <children> + <tableViewTemplate> + <name>Table</name> + <columns> + <neonTableColumn> + <name>86497fa4-acda-4041-8cbc-8d11838b4876</name> + <entityField>BLACKLIST_TYPE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>304b2589-aabf-42f6-916f-a69cff5a1b00</name> + <entityField>START_DATE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>fd5077c1-942b-42f0-8f44-80e9c568db44</name> + <entityField>END_DATE</entityField> + </neonTableColumn> + <neonTableColumn> + <name>d7050f91-da78-4ffc-a31c-30985d808473</name> + <entityField>REASON</entityField> + </neonTableColumn> + </columns> + </tableViewTemplate> + </children> +</neonView> diff --git a/process/Bulkmail_lib/process.js b/process/Bulkmail_lib/process.js index 79427698a5871ead92f07e407e4b5e383cbe4e49..e2f169b247f42930d8870f0f15c789c10625d430 100644 --- a/process/Bulkmail_lib/process.js +++ b/process/Bulkmail_lib/process.js @@ -1,3 +1,6 @@ +import("system.logging"); +import("system.entities"); +import("MarketingCondition_lib"); import("system.fileIO"); import("system.project"); import("Util_lib"); @@ -18,7 +21,6 @@ import("Email_lib"); import("system.process"); import("system.notification"); import("Document_lib"); -import("system.db"); /** * Functions for bulk mails. @@ -59,57 +61,60 @@ BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pTestRun, pUser) * * @param {String} pBulkMailId <p> * Id of the bulk mail.<br> - * @param {Bool} pTestRun (optional) <p> + * @param {Bool} pIsTestRun (optional) <p> * True indicates a Testrun<br> * @return {Object} <p> * Count of sucessful and failed mails.<br> */ -BulkMailUtils.sendBulkMail = function (pBulkMailId, pTestRun) +BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun) { + if (pIsTestRun == undefined) + { + pIsTestRun = false; + } + var [templateId, subject, emailSender, createActivity, bulkMailName, useTemplateAttachments] = newSelect("DOCUMENTTEMPLATE_ID, SUBJECT, SENDER_EMAIL_ADDRESS, CREATEACTIVITIES, NAME, USE_TEMPLATE_ATTACHMENTS") .from("BULKMAIL") .where("BULKMAIL.BULKMAILID", pBulkMailId) .arrayRow(); - useTemplateAttachments = useTemplateAttachments == "1"; + useTemplateAttachments = Utils.toBoolean(useTemplateAttachments); var template = BulkMailUtils.getBulkMailTemplate(pBulkMailId, templateId, true, useTemplateAttachments); - var recipientData; var testRecipientData; - if (pTestRun) + var recipientLoadConfig = entities.createConfigForLoadingRows() + .fields(["BULKMAILRECIPIENTID", "CONTACT_ID", "EMAIL_ADDRESS", "PERSON_ID", "ORGANISATION_ID"]) + .entity("BulkmailRecipient_entity") + .provider("RecipientsToBeMailed") + .addParameter("BulkMailId_param", pBulkMailId) + .addParameter("IsTestMail_param", pIsTestRun); + + var recipientData = entities.getRows(recipientLoadConfig); + + if (pIsTestRun) { - recipientData = newSelect("BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, BULKMAILRECIPIENT.EMAIL_ADDRESS, PERSON_ID, ORGANISATION_ID") - .from("CONTACT") - .join("BULKMAILRECIPIENT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") - .where("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) - .and("BULKMAILRECIPIENT.TEST_RECIPIENT",1) - .table(); - testRecipientData = newSelect("BULKMAILTESTRECIPIENT.CONTACT_ID, BULKMAILTESTRECIPIENT.EMAIL_ADDRESS") .from("BULKMAILTESTRECIPIENT") - .where("BULKMAILTESTRECIPIENT.BULKMAIL_ID",pBulkMailId) - .table(); - - } - else - { - recipientData = newSelect("BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, BULKMAILRECIPIENT.EMAIL_ADDRESS, PERSON_ID, ORGANISATION_ID") - .from("CONTACT") - .join("BULKMAILRECIPIENT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") - .where("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) - .and("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), SqlBuilder.NOT_EQUAL()) - .and(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)) + .where("BULKMAILTESTRECIPIENT.BULKMAIL_ID", pBulkMailId) .table(); } var mailrunId = util.getNewUUID(); - - db.insertData("MAIL_RUN", ["MAIL_RUNID","OBJECT_ROWID","OBJECT_TYPE","DATE_RUN_START","STATUS","TESTRUN"], null, [mailrunId,pBulkMailId,"BULKMAIL",vars.get("$sys.date"),$KeywordRegistry.bulkMailStatus$beingSent(),(pTestRun?1:0)]); + new SqlBuilder() + .tableName("MAIL_RUN") + .insertFields({ + "MAIL_RUNID": mailrunId, + "OBJECT_ROWID": pBulkMailId, + "OBJECT_TYPE": "Bulkmail", + "DATE_RUN_START": vars.get("$sys.date"), + "STATUS": $KeywordRegistry.bulkMailStatus$beingSent(), + "TESTRUN": pIsTestRun ? 1 : 0 + }); - var contactIds = recipientData.map(function (e) {return e[1];}); + var contactIds = recipientData.map(function (recipient) {return recipient["CONTACT_ID"];}); var successIds = []; var failedIds = []; var sentDate = vars.get("$sys.date"); @@ -120,96 +125,115 @@ BulkMailUtils.sendBulkMail = function (pBulkMailId, pTestRun) var bulkMailLink = [["BulkMail", pBulkMailId]]; var activitySubject = translate.withArguments("Bulk mail \"%0\" sent", [bulkMailName]); - if (!pTestRun){ - for (let i = 0, l = recipientData.length; i < l; i++) + if (!pIsTestRun) + { + recipientData.forEach(function (recipient) + { + let isSuccess = false; + let recipientId = recipient["BULKMAILRECIPIENTID"]; + let contactId = recipient["CONTACT_ID"]; + let emailAddress = recipient["EMAIL_ADDRESS"]; + let personId = recipient["PERSON_ID"]; + let organisationId = recipient["ORGANISATION_ID"]; + let email = mails[contactId]; + let mailLogId = util.getNewUUID(); + if (email !== undefined && emailAddress) + { + email.toRecipients = [emailAddress]; + email.sender = emailSender; + email.subject = subjects[contactId]; + BulkMailUtils.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML()); + isSuccess = email.send(); + } + + //set the recipient status to 'sent' or 'failed' + new SqlBuilder() + .tableName("MAIL_LOG") + .insertFields({ + "MAIL_LOGID": mailLogId, + "MAIL_RUN_ID": mailrunId, + "CONTACT_ID": contactId, + "STATUS": isSuccess ? $KeywordRegistry.bulkMailRecipientStatus$sent() : $KeywordRegistry.bulkMailRecipientStatus$failed(), + "SENDER_EMAIL": emailSender, + "RECIPIENT_EMAIL": emailAddress, + "MAILING_SUBJECT": subjects[contactId], + "DATE_SEND": vars.get("$sys.date") + }); + + //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 && createActivity == "1") { - - let isSuccess = false; - let contactId = recipientData[i][1]; - let email = mails[contactId]; - let mailLogId = util.getNewUUID(); - if (email !== undefined && recipientData[i][2]) - { - email.toRecipients = [recipientData[i][2]]; - email.sender = emailSender; - email.subject = subjects[contactId]; - - - - this.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML()); - isSuccess = email.send(); - } - - if (recipientData[i][0]) //set the recipient status to 'sent' or 'failed' - { - - db.insertData("MAIL_LOG", ["MAIL_LOGID","MAIL_RUN_ID","CONTACT_ID","STATUS","SENDER_EMAIL","RECIPIENT_EMAIL","MAILING_SUBJECT","DATE_SEND"], null, [mailLogId,mailrunId,contactId,(isSuccess ?$KeywordRegistry.bulkMailRecipientStatus$sent(): $KeywordRegistry.bulkMailRecipientStatus$failed()),emailSender,recipientData[i][2],subjects[contactId],vars.get("$sys.date")]); - //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, recipientData[i][0]); - if (isSuccess && createActivity == "1") - { - let activityData = { - categoryKeywordId : $KeywordRegistry.activityCategory$mail(), - directionKeywordId : $KeywordRegistry.activityDirection$outgoing(), - subject : activitySubject, - content : email.body - }; - let contactLink = [[ContactUtils.getContextByPersOrg(recipientData[i][3], recipientData[i][4]), recipientData[i][1]]]; - ActivityUtils.insertNewActivity(activityData, bulkMailLink.concat(contactLink)); - } - } + let activityData = { + categoryKeywordId : $KeywordRegistry.activityCategory$mail(), + directionKeywordId : $KeywordRegistry.activityDirection$outgoing(), + subject : activitySubject, + content : email.body + }; + let contactLink = [[ContactUtils.getContextByPersOrg(personId, organisationid), contactId]]; + ActivityUtils.insertNewActivity(activityData, bulkMailLink.concat(contactLink)); } - - newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds, SqlBuilder.IN()) - .updateData(true, "BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate]); - - - newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds, SqlBuilder.IN()) - .updateData(true, "BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate]); - - newWhere("MAIL_RUN.MAIL_RUNID",mailrunId) - .updateData(true,"MAIL_RUN",["STATUS","DATE_RUN_FINISHED"],null,[$KeywordRegistry.bulkMailStatus$sent(),vars.get("$sys.date")]); - - - newWhere("BULKMAIL.BULKMAILID", pBulkMailId) - .updateData(true, "BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()]); + }); + + newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds, SqlBuilder.IN()) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailRecipientStatus$sent(), + "SENTDATE": sentDate + }); + + newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds, SqlBuilder.IN()) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailRecipientStatus$failed(), + "SENTDATE": sentDate + }); + + newWhere("MAIL_RUN.MAIL_RUNID", mailrunId) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailStatus$sent(), + "DATE_RUN_FINISHED": vars.get("$sys.date") + }); + + newWhere("BULKMAIL.BULKMAILID", pBulkMailId) + .updateFields({ + "STATUS": $KeywordRegistry.bulkMailStatus$sent() + }); + } + else + { + for (let i = 0, l = recipientData.length; i < l; i++) + { - }else{ - for (let i = 0, l = recipientData.length; i < l; i++){ - logging.log("here"); - let isSuccess = false; - let contactId = recipientData[i][1]; - let email = mails[contactId]; - - - if (email !== undefined) - { - logging.log("email not undefined"); - logging.log(JSON.stringify(testRecipientData)); - email.sender = emailSender; - email.subject = "Test: "+subjects[contactId]; - for (let j =0; j<testRecipientData.length;j++){ - if(testRecipientData[j][1]){ - email.toRecipients = [testRecipientData[j][1]]; - isSuccess = email.send(); - - Array.prototype.push.call(isSuccess ? successIds : failedIds, recipientData[i][0]); - - if(testRecipientData[j][0]){ - let mailLogId = util.getNewUUID(); - db.insertData("MAIL_LOG", ["MAIL_LOGID","MAIL_RUN_ID","CONTACT_ID","STATUS","SENDER_EMAIL","RECIPIENT_EMAIL","MAILING_SUBJECT","DATE_SEND"], null, [mailLogId,mailrunId,testRecipientData[j][0],(isSuccess ?$KeywordRegistry.bulkMailRecipientStatus$sent(): $KeywordRegistry.bulkMailRecipientStatus$failed()),emailSender,testRecipientData[j][1],email.subject,vars.get("$sys.date")]); - this.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML()); - } + let isSuccess = false; + let contactId = recipientData[i][1]; + let email = mails[contactId]; + + if (email !== undefined) + { + email.sender = emailSender; + email.subject = "Test: "+subjects[contactId]; + for (let j =0; j<testRecipientData.length;j++){ + if(testRecipientData[j][1]){ + email.toRecipients = [testRecipientData[j][1]]; + isSuccess = email.send(); + + Array.prototype.push.call(isSuccess ? successIds : failedIds, recipientData[i][0]); + + if (testRecipientData[j][0]) + { + let mailLogId = util.getNewUUID(); + db.insertData("MAIL_LOG", ["MAIL_LOGID","MAIL_RUN_ID","CONTACT_ID","STATUS","SENDER_EMAIL","RECIPIENT_EMAIL","MAILING_SUBJECT","DATE_SEND"], null, [mailLogId,mailrunId,testRecipientData[j][0],(isSuccess ?$KeywordRegistry.bulkMailRecipientStatus$sent(): $KeywordRegistry.bulkMailRecipientStatus$failed()),emailSender,testRecipientData[j][1],email.subject,vars.get("$sys.date")]); + this.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML()); } - } - - } + } } - newWhere("MAIL_RUN.MAIL_RUNID",mailrunId) - .updateData(true,"MAIL_RUN",["STATUS","DATE_RUN_FINISHED"],null,[$KeywordRegistry.bulkMailStatus$sent(),vars.get("$sys.date")]); + } } + newWhere("MAIL_RUN.MAIL_RUNID",mailrunId) + .updateData(true,"MAIL_RUN",["STATUS","DATE_RUN_FINISHED"],null,[$KeywordRegistry.bulkMailStatus$sent(),vars.get("$sys.date")]); + + } return { sucessful : successIds.length, diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index 551fda9451a78bd17c16bd403f32ea7c48b3a288..c653469b40f1867b5889e7c3d60a77802e30d897 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -376,4 +376,7 @@ $KeywordRegistry.interestStatus$inactive = function(){return "INTEREST_INACTIVE" $KeywordRegistry.interestLinkStatus = function(){return "InterestLinkStatus";}; $KeywordRegistry.interestLinkStatus$subscribed = function(){return "INTERESTLINK_SUBSCRIBED";}; -$KeywordRegistry.interestLinkStatus$notSubscribed = function(){return "INTERESTLINK_NOTSUBSCRIBED";}; \ No newline at end of file +$KeywordRegistry.interestLinkStatus$notSubscribed = function(){return "INTERESTLINK_NOTSUBSCRIBED";}; + +$KeywordRegistry.communicationBlacklistType = function(){return "CommunicationBlacklistType";}; +$KeywordRegistry.communicationBlacklistType$emailRecipientFilter = function(){return "BLACKLIST_TYPE_EMAILRECIPIENT_FILTER";}; \ No newline at end of file diff --git a/process/MarketingCondition_lib/MarketingCondition_lib.aod b/process/MarketingCondition_lib/MarketingCondition_lib.aod index 3644f27fcc64d6312777024bbb7304b85749f27a..5e55b885b065114498b81f6bb9b0c95e4eafbb67 100644 --- a/process/MarketingCondition_lib/MarketingCondition_lib.aod +++ b/process/MarketingCondition_lib/MarketingCondition_lib.aod @@ -3,6 +3,7 @@ <name>MarketingCondition_lib</name> <majorModelMode>DISTRIBUTED</majorModelMode> <process>%aditoprj%/process/MarketingCondition_lib/process.js</process> + <alias>Data_alias</alias> <variants> <element>LIBRARY</element> </variants> diff --git a/process/MarketingCondition_lib/process.js b/process/MarketingCondition_lib/process.js index 8e14db2b5057fabbffe1b5bd71d1340c32ae5326..b6dfc6cd6aa52a0818f2e148004ecad454b82c9d 100644 --- a/process/MarketingCondition_lib/process.js +++ b/process/MarketingCondition_lib/process.js @@ -1,13 +1,203 @@ +import("Util_lib"); +import("JditoFilter_lib"); +import("system.vars"); +import("Sql_lib"); +import("KeywordRegistry_basic"); -function MarketingCondition () +/** + * Object for building communication settings sql conditions. + */ +function CommunicationSettingsCondition () { + this._contactIdSql = "CONTACT.CONTACTID"; + this._channelType = null; + this._medium = null; + this._channelIdSql = null; + this._settingStatus = null; + this._existsOperator = SqlBuilder.EXISTS(); +} + +CommunicationSettingsCondition.prototype.contactIdField = function (pContactIdSql) +{ + this._contactIdSql = pContactIdSql; + return this; +} + +CommunicationSettingsCondition.prototype.existNoSettings = function () +{ + this._existsOperator = SqlBuilder.NOT_EXISTS(); + return this; +} + +CommunicationSettingsCondition.prototype.existSettings = function () +{ + this._existsOperator = SqlBuilder.EXISTS(); + return this; +} + +/** + * Sets the medium and the source of the communication address for filtering the communication settings. + * + * @param {String} pMedium communication medium + * @param {String} pAddressSql sql field or expression for the communication address + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.medium = function (pMedium, pAddressSql) +{ + this._channelType = $KeywordRegistry.communicationChannelType$communication(); + this._medium = pMedium; + if (pAddressSql) + this._channelIdSql = pAddressSql; + return this; +} + +/** + * Sets the medium to email and the source of the email address for filtering the communication settings. + * + * @param {String} pAddressSql sql field or expression for the email address + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.emails = function (pAddressSql) +{ + return this.medium($KeywordRegistry.communicationMediumCampaign$mail(), pAddressSql); +} + +/** + * Sets the source of the address to filter the communication settings by address. + * + * @param {String} pAddressSql sql field or expression for the address id + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.address = function (pAddressSql) +{ + this._channelType = $KeywordRegistry.communicationChannelType$address(); + this._channelIdSql = pAddressSql; + return this; +} + +/** + * Only fetch communication settings with the status 'rejected' + * + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.rejected = function () +{ + this._settingStatus = $KeywordRegistry.communicationSettingStatus$rejected(); + return this; +} + +/** + * Only fetch communication settings with the status 'allowed' + * + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.allowed = function () +{ + this._settingStatus = $KeywordRegistry.communicationSettingStatus$allowed(); + return this; +} + +/** + * Only fetch communication settings with the status 'pending' + * + * @return {CommunicationSettingsCondition} current object + */ +CommunicationSettingsCondition.prototype.pending = function () +{ + this._settingStatus = $KeywordRegistry.communicationSettingStatus$pending(); + return this; +} + +/** + * Builds a sub select for fetching the communication settings with the properies as configured. + * + * @param {String|Array} [pSelectFields=COMMUNICATIONSETTINGSID] columns to select + * @return {SqlBuilder} SqlBuilder object with the full sql select + */ +CommunicationSettingsCondition.prototype.buildSelect = function (pSelectFields) +{ + if (pSelectFields == undefined) + pSelectFields = "COMMUNICATIONSETTINGS.COMMUNICATIONSETTINGSID"; + var sql = newSelect(pSelectFields) + .from("COMMUNICATIONSETTINGS") + .where("COMMUNICATIONSETTINGS.CONTACT_ID = " + this._contactIdSql) + .andIfSet("COMMUNICATIONSETTINGS.STATUS", this._settingStatus); + + var channelCondition = newWhere("COMMUNICATIONSETTINGS.CHANNEL_TYPE", $KeywordRegistry.communicationChannelType$global()); + if (this._channelType) + { + var specificChannelCondition = newWhere("COMMUNICATIONSETTINGS.CHANNEL_TYPE", this._channelType) + .andIfSet("COMMUNICATIONSETTINGS.MEDIUM", this._medium); + if (this._channelIdSql) + { + specificChannelCondition.and(newWhere("COMMUNICATIONSETTINGS.CHANNEL_ID = " + this._channelIdSql) + .or("COMMUNICATIONSETTINGS.CHANNEL_ID is null")); + } + channelCondition.or(specificChannelCondition); + } + sql.and(channelCondition); + return sql; +} + +/** + * Builds a sql condition for fetching only contacts with communication settings that have the configured properties. + * + * @return {SqlBuilder} sql condition + */ +CommunicationSettingsCondition.prototype.buildCondition = function () +{ + return newWhere(null, this.buildSelect(), this._existsOperator); } + /* required functionality: * - make communication settings condition * - make interest condition * - make blacklist condition * - make full condition * - check for single contact - */ \ No newline at end of file + */ + +function CommunicationBlacklist () +{ + this.filter = null; +} + +CommunicationBlacklist.getMailRecipientBlacklist = function () +{ + var currentDate = vars.get("$sys.date"); + var filters = newSelect("BLACKLIST_FILTER") + .from("COMMUNICATIONBLACKLIST") + .where("COMMUNICATIONBLACKLIST.BLACKLIST_TYPE", $KeywordRegistry.communicationBlacklistType$contactFilter()) + .and(newWhere("COMMUNICATIONBLACKLIST.START_DATE", currentDate, SqlBuilder.GREATER_OR_EQUAL()) + .or("COMMUNICATIONBLACKLIST.START_DATE is null")) + .and(newWhere("COMMUNICATIONBLACKLIST.END_DATE", currentDate, SqlBuilder.LESS_OR_EQUAL())) + .or("COMMUNICATIONBLACKLIST.END_DATE is null") + .table(); + + var filterMappingFn = function ([blacklistFilter]) + { + blacklistFilter = JSON.parse(blacklistFilter); + if (!blacklistFilter.filter || blacklistFilter.entity != "AnyContact" || Utils.isNullOrEmpty(blacklistFilter.filter.childs)) + return null; + + return blacklistFilter.filter; + }; + + var blacklist = new CommunicationBlacklist(); + blacklist.filter = { + type: "group", + operator: "AND", + childs: filters.map(filterMappingFn).filter(Utils.isObject) + }; + return blacklist; +} + +CommunicationBlacklistCondition.prototype.buildCondition = function () +{ + return new FilterSqlTranslator() + .filter(this.filter) + .table("BULKMAILRECIPIENT") + .getSqlCondition(); +} diff --git a/process/MarketingCondition_test/MarketingCondition_test.aod b/process/MarketingCondition_test/MarketingCondition_test.aod new file mode 100644 index 0000000000000000000000000000000000000000..e1fabb654e0c28f0747afcbf030302c26d996392 --- /dev/null +++ b/process/MarketingCondition_test/MarketingCondition_test.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> + <name>MarketingCondition_test</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/MarketingCondition_test/process.js</process> + <variants> + <element>EXECUTABLE</element> + </variants> +</process> diff --git a/process/MarketingCondition_test/process.js b/process/MarketingCondition_test/process.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/process/SetCommunicationSetting_workflowService/SetCommunicationSetting_workflowService.aod b/process/SetCommunicationSetting_workflowService/SetCommunicationSetting_workflowService.aod new file mode 100644 index 0000000000000000000000000000000000000000..ded82a9d7a27ef0c55d1456a27778be078d51da5 --- /dev/null +++ b/process/SetCommunicationSetting_workflowService/SetCommunicationSetting_workflowService.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.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> + <name>SetCommunicationSetting_workflowService</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/SetCommunicationSetting_workflowService/process.js</process> + <alias>Data_alias</alias> + <variants> + <element>WORKFLOW</element> + </variants> +</process> diff --git a/process/SetCommunicationSetting_workflowService/process.js b/process/SetCommunicationSetting_workflowService/process.js new file mode 100644 index 0000000000000000000000000000000000000000..0af3ff5f171ea831dc43b0f859acb6bde6ed5cb2 --- /dev/null +++ b/process/SetCommunicationSetting_workflowService/process.js @@ -0,0 +1,8 @@ +import("KeywordRegistry_basic"); +import("system.vars"); + +var variables = JSON.parse(vars.get("$local.value")); +var contactId = variables.contactId || variables.targetId; +var channelType = variables.channelType; +var channelId = variables.channelId; +var status = variables.status || $KeywordRegistry.communicationSettingStatus$rejected(); \ No newline at end of file diff --git a/process/SetInterestLink_workflowService/SetInterestLink_workflowService.aod b/process/SetInterestLink_workflowService/SetInterestLink_workflowService.aod new file mode 100644 index 0000000000000000000000000000000000000000..b86df5e7047ab1a0cc534a1f4dfbad242e2d94a7 --- /dev/null +++ b/process/SetInterestLink_workflowService/SetInterestLink_workflowService.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.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> + <name>SetInterestLink_workflowService</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/SetInterestLink_workflowService/process.js</process> + <alias>Data_alias</alias> + <variants> + <element>WORKFLOW</element> + </variants> +</process> diff --git a/process/SetInterestLink_workflowService/process.js b/process/SetInterestLink_workflowService/process.js new file mode 100644 index 0000000000000000000000000000000000000000..5a6447a1a3ecfafc87bf0ab271a52683c138bf81 --- /dev/null +++ b/process/SetInterestLink_workflowService/process.js @@ -0,0 +1,34 @@ +import("KeywordRegistry_basic"); +import("system.vars"); +import("system.util"); +import("Sql_lib"); + +var variables = JSON.parse(vars.get("$local.value")); +var contactId = variables.contactId || variables.targetId; +var interestId = variables.interestId; +var status = variables.status || $KeywordRegistry.interestLinkStatus$notSubscribed(); + +var interestLinkId = new SqlBuilder() + .select("INTERESTLINKID") + .from("INTERESTLINK") + .where("INTERESTLINK.CONTACT_ID", contactId) + .and("INTERESTLINK.INTEREST_ID", interestId) + .and("INTERESTLINK.STATUS", status) + .cell(); + +if (interestLinkId) +{ + newWhere("INTERESTLINK.INTERESTLINKID", interestLinkId) + .updateFields({"STATUS": status}); +} +else +{ + new SqlBuilder() + .tableName("INTERESTLINK") + .insertFields({ + "INTERESTLINKID": util.getNewUUID(), + "INTEREST_ID": interestId, + "CONTACT_ID": contactId, + "STATUS": status + }); +} \ No newline at end of file diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js index f3708067a34ad01930d3ade922c9520011e1bb91..df4abe68531e7ae543e40806661fde43e4c967c9 100644 --- a/process/Sql_lib/process.js +++ b/process/Sql_lib/process.js @@ -1017,6 +1017,22 @@ SqlBuilder.prototype.selectDistinct = function (pFields) return this; } +/** + * Sets the select clause to "select count(...)" + * @param {String} [pField=*] sql column to count, if omitted "count(*)" will be used + * @return {SqlBuilder} current SqlBuilder object + */ +SqlBuilder.prototype.selectCount = function (pField) +{ + if (pField == undefined) + { + pField = "*"; + } + this._select = SqlBuilder._getStatement(pField, "select count(", ")", true, true); + return this; +} + + /** * sets an alias-name which is added at some places if this SqlBuilder is used as subselect (e.g. in .select(), .join(), .from(), ...) * @param {String} pSubselectAlias diff --git a/process/gitLabWebhook_rest/gitLabWebhook_rest.aod b/process/gitLabWebhook_rest/gitLabWebhook_rest.aod new file mode 100644 index 0000000000000000000000000000000000000000..76c89ec8f521bfb246461ecd2ce39c83d21954e2 --- /dev/null +++ b/process/gitLabWebhook_rest/gitLabWebhook_rest.aod @@ -0,0 +1,16 @@ +<?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.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2"> + <name>gitLabWebhook_rest</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/gitLabWebhook_rest/process.js</process> + <publishAsWebservice v="true" /> + <style>REST</style> + <restAcceptedMimeType>application/json</restAcceptedMimeType> + <restDeliveredMimeType>application/json</restDeliveredMimeType> + <loginTypeId> + <element>internal.none</element> + </loginTypeId> + <variants> + <element>EXECUTABLE</element> + </variants> +</process> diff --git a/process/gitLabWebhook_rest/process.js b/process/gitLabWebhook_rest/process.js new file mode 100644 index 0000000000000000000000000000000000000000..696a523a0c55d16fc54e2686d819cb5ca2f56a15 --- /dev/null +++ b/process/gitLabWebhook_rest/process.js @@ -0,0 +1,134 @@ +import("system.auth"); +import("system.net"); +import("system.util"); +import("system.logging"); + +function restpost (pRequest) +{ + var config = { + active: true, + gitlabToken: "youtube.com/watch?v=dQw4w9WgXcQ", + teamsUrl: "https://aditosoftware.webhook.office.com/webhookb2/85493214-0e4b-4577-8fc4-0e421a2dc196@c7308693-318b-4725-9e59-73b455c6ae05/IncomingWebhook/66a53e7f47c040eda146b372c742169b/70528b00-bbf1-45e2-9925-0cc55ff8951b", + push: { + active: true, + targetBranchRegExp: null + + }, + mergeRequest: { + active: true, + onlyUnassigned: true, + excludeWIP: true, + priorityLabels: ["critical"] + }, + tag: { + active: true + } + }; + + logging.log(pRequest); + var request = JSON.parse(pRequest); + var secretToken = request.header["X-Gitlab-Token"] || request.header["X-gitlab-token"]; + if (secretToken != config.gitlabToken) + { + request.response.httpStatusCode = 403; + return JSON.stringify(request); + } + var body = JSON.parse(util.decodeBase64String(request.body)); + var teamsMessage = _getMessage(body, config); + if (teamsMessage) + { + var authConfig = auth.createConfigForNoAuth(); + var restConfig = net.createConfigForRestWebserviceCall() + .actionType(net.POST) + .url(config.teamsUrl) + .dataTypeSend("application/json") + .dataTypeJDitoSend(util.DATA_TEXT) + .requestEntity(JSON.stringify(teamsMessage)); + logging.log(JSON.stringify(teamsMessage)) + net.callRestWebservice(restConfig, authConfig); + } + + request.response.httpStatusCode = 200; + return JSON.stringify(request); + + function _getMessage (pEvent, pConfig) + { + if (!pConfig.active) + return null; + + var message = { + "@context": "https://schema.org/extensions", + "@type": "MessageCard", + "title": "Merged branch whatever into whatever", + "text": "-link- merged by That Guy", + "sections": [{ + "activityTitle": "420 commits added" + }] + }; + + if (pEvent.object_kind == "push") + return _getPushMessage(pEvent, pConfig.push); + if (pEvent.object_kind == "merge_request") + return _getMergeRequestMessage(pEvent, pConfig.mergeRequest); + if (pEvent.object_kind == "tag_push") + return _getTagMessage(pEvent, pConfig.tag); + + return null; + + function _getPushMessage (pEvent, pConfig) + { + if (!pConfig.active) + return null; + return null; + } + + function _getMergeRequestMessage (pEvent, pConfig) + { + if (!pConfig.active || pEvent.object_attributes.action != "open") + return null; + + var info = pEvent.object_attributes; + var labels = pEvent.labels.map(function (label) + { + return label.title; + }); + var isCritical = pConfig.priorityLabels.some(function (prioLabel) {return labels.includes(prioLabel);}); + var isUnassigned = !pEvent.assignees || pEvent.assignees.length === 0; + var isMessageRequired = isCritical + || ((!pConfig.onlyUnassigned || isUnassigned) + && (!pConfig.excludeWIP || !info.work_in_progress)) + if (!isMessageRequired) + return null; + + var objectTitle = "Merge Request"; + if (isCritical) + objectTitle = "Critical Merge Request"; + else if (isUnassigned) + objectTitle = "Unassigned Merge Request"; + + var title = objectTitle + " opened by " + pEvent.user.name; + var message = { + "@context": "https://schema.org/extensions", + "@type": "MessageCard", + "summary": title, + "sections": [{ + "activityTitle": title + " in <a href=\"" + pEvent.project.web_url + "\">" + pEvent.project.path_with_namespace + "</a>", + "activitySubtitle": "<a href=\"" + info.url + "\">!" + info.iid + "</a> " + info.title + + " | Request to merge " + info.source_branch + " into " + info.target_branch + }] + }; + if (isCritical) + message.themeColor = "F00420"; + + return message; + } + + function _getTagMessage (pEvent, pConfig) + { + if (!pConfig.active) + return null; + return null; + } + } +} +