From c76532b7094a74c07b0ce9107ddc3b9633a06c5b Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Thu, 28 Apr 2011 21:29:37 -0400
Subject: [PATCH] Issue #800434 by bar.hanssens, pillarsdotnet: Allow hook_mail_alter implementations to cancel mail.

---
 includes/mail.inc                    |    9 +++++++--
 modules/simpletest/simpletest.module |   13 +++++++++++++
 modules/simpletest/tests/mail.test   |   20 +++++++++++++++++++-
 modules/system/system.api.php        |    7 ++++++-
 4 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/includes/mail.inc b/includes/mail.inc
index d2febed39686c9bf3f6f7a2bf99fa1377d09f4de..be71703b5beddd4ac0d6f86839a8fc249d61db5d 100644
--- a/includes/mail.inc
+++ b/includes/mail.inc
@@ -57,6 +57,9 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
  *     user_mail_tokens($variables, $data, $options);
  *     switch($key) {
  *       case 'notice':
+ *         if (example_user_notices_optout($data['user'])) {
+ *           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));
@@ -87,7 +90,8 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
  *   Sets From to this value, if given.
  * @param $send
  *   Send the message directly, without calling drupal_mail_system()->mail()
- *   manually.
+ *   manually.  Modules implementing hook_mail_alter() may cancel sending by
+ *   setting $message['cancel'] to TRUE.
  *
  * @return
  *   The $message array structure containing all details of the
@@ -108,6 +112,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
     'from'     => isset($from) ? $from : $default_from,
     'language' => $language,
     'params'   => $params,
+    'cancel'   => !$send,
     'subject'  => '',
     'body'     => array()
   );
@@ -147,7 +152,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
   $message = $system->format($message);
 
   // Optionally send e-mail.
-  if ($send) {
+  if ($send && empty($message['cancel'])) {
     $message['result'] = $system->mail($message);
 
     // Log errors
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index b992fd2a09a80f17a0c93dce57b8ac90cd63b2b5..57e9892be20821e6168290131326a63433fe99df 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -502,3 +502,16 @@ function simpletest_clean_results_table($test_id = NULL) {
   }
   return 0;
 }
+
+/**
+ * Implements hook_mail_alter().
+ *
+ * Aborts sending of messages with module='simpletest' and key='cancel_test'.
+ *
+ * @see MailTestCase::testCancelMessage()
+ */
+function simpletest_mail_alter(&$message) {
+  if ($message['id'] == 'simpletest_cancel_test') {
+    $message['cancel'] = TRUE;
+  }
+}
diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test
index 8a7b152d9d32eee7ae47c9ef8b5fb9c77f4e0cf1..69a062557274d2990204e4d99ee9e00ce7defd06 100644
--- a/modules/simpletest/tests/mail.test
+++ b/modules/simpletest/tests/mail.test
@@ -21,7 +21,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'));
@@ -41,6 +41,24 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
   }
 
   /**
+   * 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@drupal.org', $language);
+
+    // Assert that the message was not actually sent.
+    $this->assertNull(self::$sent_message, 'Message cancellation works: <pre>' . var_export(self::$sent_message,1) . '</pre>');
+  }
+
+  /**
    * Concatenate and wrap the e-mail body for plain-text mails.
    *
    * @see DefaultMailSystem
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index 22ad7a61633288c4e4379daa591ec8ad76ca522a..33487cd7d684d1249564784ff5532fe659de8d8f 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -1795,7 +1795,7 @@ function hook_image_toolkits() {
  * invoke hook_mail_alter(). For example, a contributed module directly
  * calling the drupal_mail_system()->mail() or PHP mail() function
  * will not invoke this hook. All core modules use drupal_mail() for
- * messaging, it is best practice but not mandatory in contributed modules.
+ * messaging; it is best practice but not mandatory in contributed modules.
  *
  * @param $message
  *   An array containing the message data. Keys in this array include:
@@ -1824,11 +1824,16 @@ function hook_image_toolkits() {
  *  - 'language':
  *     The language object used to build the message before hook_mail_alter()
  *     is invoked.
+ *  - 'cancel':
+ *     Set to TRUE to abort sending this message.
  *
  * @see drupal_mail()
  */
 function hook_mail_alter(&$message) {
   if ($message['id'] == 'modulename_messagekey') {
+    if (!modulename_messagekey_optin($message['to'])) {
+      $message['cancel'] = TRUE;
+    }
     $message['body'][] = "--\nMail sent out from " . variable_get('sitename', t('Drupal'));
   }
 }
-- 
1.7.1

