From 7b83575c3e471770b0fd855238e7d908a147a6e0 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 17 Aug 2020 13:13:33 +0100 Subject: [PATCH 1/7] CATL-1614: Add new field to the mail account form - Do not create new contacts when filing emails --- CRM/Admin/Form/MailSettings.php | 4 ++ templates/CRM/Admin/Form/MailSettings.tpl | 77 +++++++++++++---------- xml/schema/Core/MailSettings.xml | 11 ++++ 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/CRM/Admin/Form/MailSettings.php b/CRM/Admin/Form/MailSettings.php index 4de49fe41356..3ec4e11d0a52 100644 --- a/CRM/Admin/Form/MailSettings.php +++ b/CRM/Admin/Form/MailSettings.php @@ -70,6 +70,8 @@ public function buildQuickForm() { ]; $this->add('select', 'is_default', ts('Used For?'), $usedfor); $this->addField('activity_status', ['placeholder' => FALSE]); + + $this->add('checkbox', 'is_contact_creation_disabled_if_no_match', ts('Do not create new contacts when filing emails')); } /** @@ -146,6 +148,7 @@ public function postProcess() { 'is_ssl', 'is_default', 'activity_status', + 'is_contact_creation_disabled_if_no_match', ]; $params = []; @@ -153,6 +156,7 @@ public function postProcess() { if (in_array($f, [ 'is_default', 'is_ssl', + 'is_contact_creation_disabled_if_no_match', ])) { $params[$f] = CRM_Utils_Array::value($f, $formValues, FALSE); } diff --git a/templates/CRM/Admin/Form/MailSettings.tpl b/templates/CRM/Admin/Form/MailSettings.tpl index 361ffe09e187..1e2a4deef9fa 100644 --- a/templates/CRM/Admin/Form/MailSettings.tpl +++ b/templates/CRM/Admin/Form/MailSettings.tpl @@ -9,62 +9,71 @@ *} {* this template is used for adding/editing email settings. *}
-
{include file="CRM/common/formButtons.tpl" location="top"}
-{if $action eq 8} -
+
{include file="CRM/common/formButtons.tpl" location="top"}
+ {if $action eq 8} +
{icon icon="fa-info-circle"}{/icon} - {ts}WARNING: Deleting this option will result in the loss of mail settings data.{/ts} {ts}Do you want to continue?{/ts} -
+ {ts}WARNING: Deleting this option will result in the loss of mail settings data.{/ts} {ts}Do you want to continue?{/ts} +
{include file="CRM/common/formButtons.tpl" location="top"}
-{else} + {else} - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + +
{$form.name.label}{$form.name.html}
 {ts}Name of this group of settings.{/ts}
{$form.name.label}{$form.name.html}
 {ts}Name of this group of settings.{/ts}
{$form.server.label}{$form.server.html}
 {ts}Name or IP address of mail server machine.{/ts}
{$form.server.label}{$form.server.html}
 {ts}Name or IP address of mail server machine.{/ts}
{$form.username.label}{$form.username.html}
 {ts}Username to use when polling (for IMAP and POP3).{/ts}
{$form.username.label}{$form.username.html}
 {ts}Username to use when polling (for IMAP and POP3).{/ts}
{$form.password.label}{$form.password.html}
 {ts}Password to use when polling (for IMAP and POP3).{/ts}
{$form.password.label}{$form.password.html}
 {ts}Password to use when polling (for IMAP and POP3).{/ts}
{$form.localpart.label}{$form.localpart.html}
 {ts}Optional local part (e.g., 'civimail+' for addresses like civimail+s.1.2@example.com).{/ts}
{$form.localpart.label}{$form.localpart.html}
 {ts}Optional local part (e.g., 'civimail+' for addresses like civimail+s.1.2@example.com).{/ts}
{$form.domain.label}{$form.domain.html}
 {ts}Email address domain (the part after @).{/ts}
{$form.domain.label}{$form.domain.html}
 {ts}Email address domain (the part after @).{/ts}
{$form.return_path.label}{$form.return_path.html}
 {ts}Contents of the Return-Path header.{/ts}
{$form.return_path.label}{$form.return_path.html}
 {ts}Contents of the Return-Path header.{/ts}
{$form.protocol.label}{$form.protocol.html}
 {ts}Name of the protocol to use for polling.{/ts}
{$form.protocol.label}{$form.protocol.html}
 {ts}Name of the protocol to use for polling.{/ts}
{$form.source.label}{$form.source.html}
 {ts}Folder to poll from when using IMAP (will default to INBOX when empty), path to poll from when using Maildir, etc..{/ts}
{$form.source.label}{$form.source.html}
 {ts}Folder to poll from when using IMAP (will default to INBOX when empty), path to poll from when using Maildir, etc..{/ts}
{$form.is_ssl.label}{$form.is_ssl.html}
 {ts}Whether to use SSL for IMAP and POP3 or not.{/ts}
{$form.is_ssl.label}{$form.is_ssl.html}
 {ts}Whether to use SSL for IMAP and POP3 or not.{/ts}
{$form.is_default.label}{$form.is_default.html}
 {ts}How this mail account will be used. Only one box may be used for bounce processing. It will also be used as the envelope email when sending mass mailings.{/ts}
{$form.is_default.label}{$form.is_default.html}
 {ts}How this mail account will be used. Only one box may be used for bounce processing. It will also be used as the envelope email when sending mass mailings.{/ts}
{$form.activity_status.label}{$form.activity_status.html}
 {$form.is_contact_creation_disabled_if_no_match.html}{$form.is_contact_creation_disabled_if_no_match.label}
{$form.activity_status.label}{$form.activity_status.html}
-
{include file="CRM/common/formButtons.tpl" location="bottom"}
-{/if} + +
{include file="CRM/common/formButtons.tpl" location="bottom"}
+ {/if}
+ {literal} {/literal} diff --git a/xml/schema/Core/MailSettings.xml b/xml/schema/Core/MailSettings.xml index 0dea26c13b2a..bad887e5c779 100644 --- a/xml/schema/Core/MailSettings.xml +++ b/xml/schema/Core/MailSettings.xml @@ -150,4 +150,15 @@ Select + + is_contact_creation_disabled_if_no_match + boolean + Do not create new contacts when filing emails + 0 + + CheckBox + + If this option is enabled, CiviCRM will not create new contacts when filing emails. + 5.30 + From ca3418455f50f3fd70a667bbefdd8095bf5daf12 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 17 Aug 2020 13:25:11 +0100 Subject: [PATCH 2/7] CATL-1614: Add upgrader to add new field to db table - is_contact_creation_disabled_if_no_match --- CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl diff --git a/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl new file mode 100644 index 000000000000..1f4bdf572111 --- /dev/null +++ b/CRM/Upgrade/Incremental/sql/5.30.0.mysql.tpl @@ -0,0 +1,4 @@ +{* file to handle db changes in 5.30.0 during upgrade *} + +ALTER TABLE civicrm_mail_settings +ADD is_contact_creation_disabled_if_no_match TINYINT default 0 not null comment 'If this option is enabled, CiviCRM will not create new contacts when filing emails'; From 54112b7a59ea129a61c16f1f72337c70dbfef101 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 17 Aug 2020 16:25:32 +0100 Subject: [PATCH 3/7] CATL-1614: Update inbound email processing - add parameter to make creating contact optional --- CRM/Utils/Mail/EmailProcessor.php | 5 +++-- CRM/Utils/Mail/Incoming.php | 17 ++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CRM/Utils/Mail/EmailProcessor.php b/CRM/Utils/Mail/EmailProcessor.php index 9d68310b9dfe..96e3b30523af 100644 --- a/CRM/Utils/Mail/EmailProcessor.php +++ b/CRM/Utils/Mail/EmailProcessor.php @@ -157,7 +157,7 @@ public static function _process($civiMail, $dao, $is_create_activities) { try { $store = CRM_Mailing_MailStore::getStore($dao->name); } - catch (Exception$e) { + catch (Exception $e) { $message = ts('Could not connect to MailStore for ') . $dao->username . '@' . $dao->server . '

'; $message .= ts('Error message: '); $message .= '

' . $e->getMessage() . '

'; @@ -226,7 +226,8 @@ public static function _process($civiMail, $dao, $is_create_activities) { if ($usedfor == 0 || $is_create_activities) { // if its the activities that needs to be processed .. try { - $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail); + $createContact = !($dao->is_contact_creation_disabled_if_no_match ?? FALSE); + $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail, $createContact); } catch (Exception $e) { echo $e->getMessage(); diff --git a/CRM/Utils/Mail/Incoming.php b/CRM/Utils/Mail/Incoming.php index 1e2da042236b..8f4cc521ac1b 100644 --- a/CRM/Utils/Mail/Incoming.php +++ b/CRM/Utils/Mail/Incoming.php @@ -322,10 +322,11 @@ public function &parse(&$file) { /** * @param $mail + * @param $createContact * * @return array */ - public static function parseMailingObject(&$mail) { + public static function parseMailingObject(&$mail, $createContact = TRUE) { $config = CRM_Core_Config::singleton(); @@ -342,7 +343,7 @@ public static function parseMailingObject(&$mail) { } $params['from'] = []; - self::parseAddress($mail->from, $field, $params['from'], $mail); + self::parseAddress($mail->from, $field, $params['from'], $mail, $createContact); // we definitely need a contact id for the from address // if we dont have one, skip this email @@ -353,7 +354,7 @@ public static function parseMailingObject(&$mail) { $emailFields = ['to', 'cc', 'bcc']; foreach ($emailFields as $field) { $value = $mail->$field; - self::parseAddresses($value, $field, $params, $mail); + self::parseAddresses($value, $field, $params, $mail, $createContact); } // define other parameters @@ -396,8 +397,9 @@ public static function parseMailingObject(&$mail) { * @param array $params * @param $subParam * @param $mail + * @param $createContact */ - public static function parseAddress(&$address, &$params, &$subParam, &$mail) { + public static function parseAddress(&$address, &$params, &$subParam, &$mail, $createContact = TRUE) { // CRM-9484 if (empty($address->email)) { return; @@ -408,7 +410,7 @@ public static function parseAddress(&$address, &$params, &$subParam, &$mail) { $contactID = self::getContactID($subParam['email'], $subParam['name'], - TRUE, + $createContact, $mail ); $subParam['id'] = $contactID ? $contactID : NULL; @@ -419,13 +421,14 @@ public static function parseAddress(&$address, &$params, &$subParam, &$mail) { * @param $token * @param array $params * @param $mail + * @param $createContact */ - public static function parseAddresses(&$addresses, $token, &$params, &$mail) { + public static function parseAddresses(&$addresses, $token, &$params, &$mail, $createContact = TRUE) { $params[$token] = []; foreach ($addresses as $address) { $subParam = []; - self::parseAddress($address, $params, $subParam, $mail); + self::parseAddress($address, $params, $subParam, $mail, $createContact); $params[$token][] = $subParam; } } From 98f563dc47aab5cc51e745bd97e849a41d8cf3ea Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 18 Aug 2020 08:42:21 +0100 Subject: [PATCH 4/7] CATL-1614: Check if activity is empty before creating it (during email filing) --- CRM/Utils/Mail/EmailProcessor.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/CRM/Utils/Mail/EmailProcessor.php b/CRM/Utils/Mail/EmailProcessor.php index 96e3b30523af..a4dc3de8bc8a 100644 --- a/CRM/Utils/Mail/EmailProcessor.php +++ b/CRM/Utils/Mail/EmailProcessor.php @@ -242,16 +242,20 @@ public static function _process($civiMail, $dao, $is_create_activities) { if (!empty($dao->activity_status)) { $params['status_id'] = $dao->activity_status; } - $result = civicrm_api('activity', 'create', $params); - if ($result['is_error']) { - $matches = FALSE; - echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n"; - } - else { - $matches = TRUE; - CRM_Utils_Hook::emailProcessor('activity', $params, $mail, $result); - echo "Processed as Activity: {$mail->subject}\n"; + // Create activity if its not empty. + if (!empty($params['subject']) || !empty($params['target_contact_id'])) { + $result = civicrm_api('activity', 'create', $params); + + if ($result['is_error']) { + $matches = FALSE; + echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n"; + } + else { + $matches = TRUE; + CRM_Utils_Hook::emailProcessor('activity', $params, $mail, $result); + echo "Processed as Activity: {$mail->subject}\n"; + } } } From 9a99ddcce923ac55dd3387bf0c5198a0a4dde6c8 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 18 Aug 2020 08:51:59 +0100 Subject: [PATCH 5/7] CATL-1614: Update inbound email processing - get email params even if no matching contact found --- CRM/Utils/Mail/EmailProcessor.php | 2 +- CRM/Utils/Mail/Incoming.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CRM/Utils/Mail/EmailProcessor.php b/CRM/Utils/Mail/EmailProcessor.php index a4dc3de8bc8a..8721c943fdce 100644 --- a/CRM/Utils/Mail/EmailProcessor.php +++ b/CRM/Utils/Mail/EmailProcessor.php @@ -227,7 +227,7 @@ public static function _process($civiMail, $dao, $is_create_activities) { // if its the activities that needs to be processed .. try { $createContact = !($dao->is_contact_creation_disabled_if_no_match ?? FALSE); - $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail, $createContact); + $mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail, $createContact, FALSE); } catch (Exception $e) { echo $e->getMessage(); diff --git a/CRM/Utils/Mail/Incoming.php b/CRM/Utils/Mail/Incoming.php index 8f4cc521ac1b..a11961081250 100644 --- a/CRM/Utils/Mail/Incoming.php +++ b/CRM/Utils/Mail/Incoming.php @@ -323,10 +323,11 @@ public function &parse(&$file) { /** * @param $mail * @param $createContact + * @param $requireContact * * @return array */ - public static function parseMailingObject(&$mail, $createContact = TRUE) { + public static function parseMailingObject(&$mail, $createContact = TRUE, $requireContact = TRUE) { $config = CRM_Core_Config::singleton(); @@ -347,7 +348,7 @@ public static function parseMailingObject(&$mail, $createContact = TRUE) { // we definitely need a contact id for the from address // if we dont have one, skip this email - if (empty($params['from']['id'])) { + if ($requireContact && empty($params['from']['id'])) { return NULL; } From 9ec14bfbc7ce19174eade21bc79f23ce60a55ecd Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 20 Aug 2020 10:12:16 +0300 Subject: [PATCH 6/7] CATL-1614: Add unit test to test is_contact_creation_disabled_if_no_match option during Inbound Email Processing --- .../CRM/Utils/Mail/EmailProcessorTest.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/phpunit/CRM/Utils/Mail/EmailProcessorTest.php b/tests/phpunit/CRM/Utils/Mail/EmailProcessorTest.php index 89354b9783ce..3e10c72f2cd5 100644 --- a/tests/phpunit/CRM/Utils/Mail/EmailProcessorTest.php +++ b/tests/phpunit/CRM/Utils/Mail/EmailProcessorTest.php @@ -169,4 +169,37 @@ public function setUpMailing() { $this->eventQueue = $this->callAPISuccess('MailingEventQueue', 'get', ['api.MailingEventQueue.create' => ['hash' => 'aaaaaaaaaaaaaaaa']]); } + /** + * Set up mail account with 'Do not create new contacts when filing emails' + * option enabled. + */ + public function setUpDoNotCreateContact() { + $this->callAPISuccess('MailSettings', 'get', [ + 'api.MailSettings.create' => [ + 'name' => 'mailbox', + 'protocol' => 'Localdir', + 'source' => __DIR__ . '/data/mail', + 'domain' => 'example.com', + 'is_default' => '0', + 'is_contact_creation_disabled_if_no_match' => TRUE, + ], + ]); + } + + /** + * Test case email processing when is_non_case_email_skipped is enabled. + */ + public function testInboundProcessingDoNotCreateContact() { + $this->setUpDoNotCreateContact(); + $mail = 'test_non_cases_email.eml'; + + copy(__DIR__ . '/data/inbound/' . $mail, __DIR__ . '/data/mail/' . $mail); + $this->callAPISuccess('job', 'fetch_activities', []); + $result = civicrm_api3('Contact', 'get', [ + 'sequential' => 1, + 'email' => "from@test.test", + ]); + $this->assertTrue(empty($result['values'])); + } + } From 7ae1fc25c8ea001c7ce209cdbce02bbffc4c47e5 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 20 Aug 2020 11:30:07 +0300 Subject: [PATCH 7/7] CATL-1614: Refactor mail settings template - change 'Activity Status' label location --- templates/CRM/Admin/Form/MailSettings.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/CRM/Admin/Form/MailSettings.tpl b/templates/CRM/Admin/Form/MailSettings.tpl index 1e2a4deef9fa..9185c54a18a9 100644 --- a/templates/CRM/Admin/Form/MailSettings.tpl +++ b/templates/CRM/Admin/Form/MailSettings.tpl @@ -54,7 +54,7 @@  {$form.is_contact_creation_disabled_if_no_match.html}{$form.is_contact_creation_disabled_if_no_match.label} - {$form.activity_status.label}{$form.activity_status.html} +  {$form.activity_status.label}

{$form.activity_status.html}
{include file="CRM/common/formButtons.tpl" location="bottom"}