From 96751ed26f9f34eac45ae81299c009958a22d5c7 Mon Sep 17 00:00:00 2001 From: "Robert J. Lang" Date: Fri, 5 Apr 2024 10:17:19 -0700 Subject: [PATCH] =?UTF-8?q?Issue=20#211:=20Add=20support=20for=20CC,=20BCC?= =?UTF-8?q?,=20Reply-to=20to=20=E2=80=9CSend=20mail=E2=80=9D=20actions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/system.eval.inc | 57 +++++++++++++++++++++++++++++++++++----- modules/system.rules.inc | 48 +++++++++++++++++++++++++++++++++ tests/rules.test | 50 +++++++++++++++++++++++++++++++++-- 3 files changed, 146 insertions(+), 9 deletions(-) diff --git a/modules/system.eval.inc b/modules/system.eval.inc index a905f49..3170957 100644 --- a/modules/system.eval.inc +++ b/modules/system.eval.inc @@ -88,15 +88,28 @@ function rules_action_breadcrumb_set(array $titles, array $paths) { /** * Action Implementation: Send mail. */ -function rules_action_mail($to, $subject, $message, $from, $langcode, $settings, RulesState $state, RulesPlugin $element) { - $to = str_replace(array("\r", "\n"), '', $to); - $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL; +function rules_action_mail($to, $cc, $bcc, $reply_to, $subject, $message, $from, $langcode, $settings, RulesState $state, RulesPlugin $element) { + _rules_strip_line_breaks($to, FALSE); + + _rules_strip_line_breaks($cc); + _rules_strip_line_breaks($bcc); + _rules_strip_line_breaks($reply_to); + _rules_strip_line_breaks($from); + $params = array( 'subject' => $subject, 'message' => $message, 'langcode' => $langcode, ); - + if (!empty($cc)) { + $params['cc'] = $cc; + } + if (!empty($bcc)) { + $params['bcc'] = $bcc; + } + if (!empty($reply_to)) { + $params['reply-to'] = $reply_to; + } if (!empty($from)) { $params['from'] = $from; } @@ -116,8 +129,12 @@ function rules_action_mail($to, $subject, $message, $from, $langcode, $settings, /** * Action: Send mail to all users of a specific role group(s). */ -function rules_action_mail_to_users_of_role($roles, $subject, $message, $from, $settings, RulesState $state, RulesPlugin $element) { - $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL; +function rules_action_mail_to_users_of_role($roles, $cc, $bcc, $reply_to, $subject, $message, $from, $settings, RulesState $state, RulesPlugin $element) { + + _rules_strip_line_breaks($cc); + _rules_strip_line_breaks($bcc); + _rules_strip_line_breaks($reply_to); + _rules_strip_line_breaks($from); // All authenticated users, which is everybody. if (in_array(BACKDROP_AUTHENTICATED_ROLE, $roles)) { @@ -138,10 +155,19 @@ function rules_action_mail_to_users_of_role($roles, $subject, $message, $from, $ 'subject' => $subject, 'message' => $message, ); - + if (!empty($cc)) { + $params['cc'] = $cc; + } + if (!empty($bcc)) { + $params['bcc'] = $bcc; + } + if (!empty($reply_to)) { + $params['reply-to'] = $reply_to; + } if (!empty($from)) { $params['from'] = $from; } + // Set a unique key for this mail. $name = isset($element->root()->name) ? $element->root()->name : 'unnamed'; $key = 'rules_action_mail_to_users_of_role_' . $name . '_' . $element->elementId(); @@ -166,6 +192,13 @@ function rules_action_mail_to_users_of_role($roles, $subject, $message, $from, $ } } +/** + * Strip line breaks from a string. + */ +function _rules_strip_line_breaks(&$string, $null_if_empty = TRUE) { + $string = !empty($string) ? str_replace(array("\r", "\n"), '', $string) : ($null_if_empty ? NULL : ''); +} + /** * Implements hook_mail(). * @@ -178,6 +211,16 @@ function rules_mail($key, &$message, $params) { if (!empty($params['from'])) { $message['from'] = $params['from']; } + if (!empty($params['cc'])) { + $message['headers']['Cc'] = $params['cc']; + } + if (!empty($params['bcc'])) { + $message['headers']['Bcc'] = $params['bcc']; + } + if (!empty($params['reply-to'])) { + $message['reply-to'] = $params['reply-to']; + $message['headers']['Reply-to'] = $params['reply-to']; + } } /** diff --git a/modules/system.rules.inc b/modules/system.rules.inc index 95e0cc1..2d97c51 100644 --- a/modules/system.rules.inc +++ b/modules/system.rules.inc @@ -227,6 +227,30 @@ function rules_system_action_info() { t('User <user@example.com>, Another User <anotheruser@example.com>'), ))), ), + 'cc' => array( + 'type' => 'text', + 'label' => t('CC'), + 'description' => t("CC recipient(s) (same form as To)"), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), + 'bcc' => array( + 'type' => 'text', + 'label' => t('BCC'), + 'description' => t("BCC recipient(s) (same form as To)"), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), + 'reply_to' => array( + 'type' => 'text', + 'label' => t('Reply-to'), + 'description' => t('Reply-to address (same for as To)'), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), 'subject' => array( 'type' => 'text', 'label' => t('Subject'), @@ -268,6 +292,30 @@ function rules_system_action_info() { 'options list' => 'entity_plus_metadata_user_roles', 'description' => t('Select the roles whose users should receive the mail.'), ), + 'cc' => array( + 'type' => 'text', + 'label' => t('CC'), + 'description' => t("CC recipient(s) (same form as To)"), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), + 'bcc' => array( + 'type' => 'text', + 'label' => t('BCC'), + 'description' => t("BCC recipient(s) (same form as To)"), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), + 'reply_to' => array( + 'type' => 'text', + 'label' => t('Reply-to'), + 'description' => t('Reply-to address (same for as To)'), + 'translatable' => TRUE, + 'optional' => TRUE, + 'allow null' => TRUE, + ), 'subject' => array( 'type' => 'text', 'label' => t('Subject'), diff --git a/tests/rules.test b/tests/rules.test index e591491..7795391 100644 --- a/tests/rules.test +++ b/tests/rules.test @@ -9,7 +9,7 @@ */ class RulesTestCase extends BackdropWebTestCase { protected $normal_role; - + /** * Overrides BackdropWebTestCase::setUp(). */ @@ -1396,6 +1396,30 @@ class RulesIntegrationTestCase extends BackdropWebTestCase { config_set('rules.settings','rules_debug_log', 1); } + /** + * Asserts that the most recently sent email message has the given value in + * the mail's headers. + * + * The field in $name must have the content described in $value. + * + * @param $name + * Name of field or message property to assert. Examples: subject, body, id, ... + * @param $value + * Value of the field to assert. + * @param $message + * Message to display. + * + * @return + * TRUE on pass, FALSE on fail. + * + * @see BackdropWebTestCase::assertMail() + */ + protected function assertMailHeaders($name, $value = '', $message = '') { + $captured_emails = state_get('test_email_collector', array()); + $email = end($captured_emails); + return $this->assertTrue($email && isset($email['headers'][$name]) && $email['headers'][$name] == $value, $message, t('Email')); + } + /** * Just makes sure the access callback run without errors. */ @@ -2114,6 +2138,15 @@ class RulesIntegrationTestCase extends BackdropWebTestCase { rules_action('mail', $settings + array('from' => 'sender@example.com'))->execute(); $this->assertMail('from', 'sender@example.com', 'Specified from address has been used'); + rules_action('mail', $settings + array('cc' => 'cc@example.com'))->execute(); + $this->assertMailHeaders('Cc', 'cc@example.com', 'Specified CC address has been used'); + + rules_action('mail', $settings + array('bcc' => 'bcc@example.com'))->execute(); + $this->assertMailHeaders('Bcc', 'bcc@example.com', 'Specified BCC address has been used'); + + rules_action('mail', $settings + array('reply_to' => 'reply-to@example.com'))->execute(); + $this->assertMailHeaders('Reply-to', 'reply-to@example.com', 'Specified Reply-to address has been used'); + // Test sending mail to all users of a role. First clear the mail // collector to remove the mail sent in the previous line of code. state_set('test_email_collector', array()); @@ -2157,8 +2190,21 @@ class RulesIntegrationTestCase extends BackdropWebTestCase { $mail = array_pop($mails); $this->assertTrue($mail['to'] == $user1->mail || $mail['to'] == $user2->mail, 'Mail to user of a role has been sent.'); + // Execute action again and check that CC, BCC, and Reply-to were set. + rules_action('mail_to_users_of_role', $settings + array( + 'roles' => $roles, + 'cc' => 'cc@example.com', + 'bcc' => 'bcc@example.com', + 'reply_to' => 'reply-to@example.com', + ))->execute(); + $mails = $this->backdropGetMails(); + $mail = array_pop($mails); + $this->assertTrue($mail['headers']['Cc'] == 'cc@example.com', 'Mail to user of a role has been sent with correct CC.'); + $this->assertTrue($mail['headers']['Bcc'] == 'bcc@example.com', 'Mail to user of a role has been sent with correct BCC.'); + $this->assertTrue($mail['headers']['Reply-to'] == 'reply-to@example.com', 'Mail to user of a role has been sent with correct Reply-to.'); + // Execute action again, this time to send mail to both roles. - // This time check that three mails were sent - one for each user.. + // This time check that three mails were sent - one for each user. state_set('test_email_collector', array()); rules_action('mail_to_users_of_role', $settings + array('roles' => array_merge($roles, $additional_roles)))->execute(); $mails = $this->backdropGetMails();