From 138745688bcbed1a20b301ed616e5ed0796ecb5e Mon Sep 17 00:00:00 2001 From: webchick Date: Sat, 21 Jan 2012 10:24:52 -0800 Subject: [PATCH] Issue #800434 by pillarsdotnet, plach, bart.hanssens: Added drupal_mail(), allow hook_mail_alter() implementation to cancel mail. --- includes/mail.inc | 46 +++++++++++++++++++++++----- modules/simpletest/simpletest.module | 13 ++++++++ modules/simpletest/tests/mail.test | 24 +++++++++++++-- modules/system/system.api.php | 8 +++++ 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/includes/mail.inc b/includes/mail.inc index 7272df972e2..13a6f464390 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -57,6 +57,12 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER * user_mail_tokens($variables, $data, $options); * switch($key) { * case 'notice': + * // If the recipient can receive such notices by instant-message, do + * // not send by email. + * if (example_im_send($key, $message, $params)) { + * $message['send'] = FALSE; + * break; + * } * $langcode = $message['language']->language; * $message['subject'] = t('Notification from !site', $variables, array('langcode' => $langcode)); * $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, array('langcode' => $langcode)); @@ -65,6 +71,19 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER * } * @endcode * + * Another example, which uses drupal_mail() to format a message for sending + * later: + * + * @code + * $params = array('current_conditions' => $data); + * $to = 'user@example.com'; + * $message = drupal_mail('example', 'notice', $to, $language, $params, FALSE); + * // Only add to the spool if sending was not canceled. + * if ($message['send']) { + * example_spool_message($message); + * } + * @endcode + * * @param $module * A module name to invoke hook_mail() on. The {$module}_mail() hook will be * called to complete the $message structure which will already contain common @@ -86,8 +105,10 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER * @param $from * Sets From to this value, if given. * @param $send - * Send the message directly, without calling drupal_mail_system()->mail() - * manually. + * If TRUE, drupal_mail() will call drupal_mail_system()->mail() to deliver + * the message, and store the result in $message['result']. Modules + * implementing hook_mail_alter() may cancel sending by setting + * $message['send'] to FALSE. * * @return * The $message array structure containing all details of the @@ -108,6 +129,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N 'from' => isset($from) ? $from : $default_from, 'language' => $language, 'params' => $params, + 'send' => TRUE, 'subject' => '', 'body' => array() ); @@ -148,12 +170,20 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N // Optionally send e-mail. if ($send) { - $message['result'] = $system->mail($message); - - // Log errors - if (!$message['result']) { - watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR); - drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error'); + // The original caller requested sending. Sending was canceled by one or + // more hook_mail_alter() implementations. We set 'result' to NULL, because + // FALSE indicates an error in sending. + if (empty($message['send'])) { + $message['result'] = NULL; + } + // Sending was originally requested and was not canceled. + else { + $message['result'] = $system->mail($message); + // Log errors. + if (!$message['result']) { + watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR); + drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error'); + } } } diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index b820f530793..980bc146351 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -504,3 +504,16 @@ function simpletest_clean_results_table($test_id = NULL) { } return 0; } + +/** + * Implements hook_mail_alter(). + * + * Aborts sending of messages with ID 'simpletest_cancel_test'. + * + * @see MailTestCase::testCancelMessage() + */ +function simpletest_mail_alter(&$message) { + if ($message['id'] == 'simpletest_cancel_test') { + $message['send'] = FALSE; + } +} diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test index a6c7b40e5ef..09dcde60c0d 100644 --- a/modules/simpletest/tests/mail.test +++ b/modules/simpletest/tests/mail.test @@ -22,7 +22,7 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { } function setUp() { - parent::setUp(); + parent::setUp(array('simpletest')); // Set MailTestCase (i.e. this class) as the SMTP library variable_set('mail_system', array('default-system' => 'MailTestCase')); @@ -35,10 +35,28 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { global $language; // Use MailTestCase for sending a message. - $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language); + $message = drupal_mail('simpletest', 'mail_test', 'testing@example.com', $language); // Assert whether the message was sent through the send function. - $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.')); + $this->assertEqual(self::$sent_message['to'], 'testing@example.com', t('Pluggable mail system is extendable.')); + } + + /** + * Test that message sending may be canceled. + * + * @see simpletest_mail_alter() + */ + function testCancelMessage() { + global $language; + + // Reset the class variable holding a copy of the last sent message. + self::$sent_message = NULL; + + // Send a test message that simpletest_mail_alter should cancel. + $message = drupal_mail('simpletest', 'cancel_test', 'cancel@example.com', $language); + + // Assert that the message was not actually sent. + $this->assertNull(self::$sent_message, 'Message was canceled.'); } /** diff --git a/modules/system/system.api.php b/modules/system/system.api.php index a2e71d2ccf0..b2ae3192fe6 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1903,11 +1903,19 @@ function hook_image_toolkits() { * - 'language': * The language object used to build the message before hook_mail_alter() * is invoked. + * - 'send': + * Set to FALSE to abort sending this email message. * * @see drupal_mail() */ function hook_mail_alter(&$message) { if ($message['id'] == 'modulename_messagekey') { + if (!example_notifications_optin($message['to'], $message['id'])) { + // If the recipient has opted to not receive such messages, cancel + // sending. + $message['send'] = FALSE; + return; + } $message['body'][] = "--\nMail sent out from " . variable_get('site_name', t('Drupal')); } }