CRM-11958 - Add hook_civicrm_alterMailer (with unit-test)
authorTim Otten <totten@civicrm.org>
Thu, 5 Sep 2013 07:12:11 +0000 (00:12 -0700)
committerTim Otten <totten@civicrm.org>
Thu, 5 Sep 2013 07:12:11 +0000 (00:12 -0700)
----------------------------------------
* CRM-11958: New hook implementation for altering outgoing mail configuration
  http://issues.civicrm.org/jira/browse/CRM-11958

CRM/Core/Config.php
CRM/Utils/FakeObject.php [new file with mode: 0644]
CRM/Utils/Hook.php
tests/phpunit/CRM/Core/Config/MailerTest.php [new file with mode: 0644]
tests/phpunit/CiviTest/CiviUnitTestCase.php

index b39b9bff9b7c144193c6499c6994529e955eb825..be3bf7c53cf052d002f48c792b0836a9cfcd753b 100644 (file)
@@ -113,7 +113,7 @@ class CRM_Core_Config extends CRM_Core_Config_Variables {
    * the handle on the mail handler that we are using
    * @var object
    */
-  private static $_mail = NULL;
+  public static $_mail = NULL;
 
   /**
    * We only need one instance of this object. So we use the singleton
@@ -520,7 +520,7 @@ class CRM_Core_Config extends CRM_Core_Config_Variables {
       if ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB ||
         (defined('CIVICRM_MAILER_SPOOL') && CIVICRM_MAILER_SPOOL)
       ) {
-        self::$_mail = new CRM_Mailing_BAO_Spool();
+        self::$_mail = self::_createMailer('CRM_Mailing_BAO_Spool', array());
       }
       elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SMTP) {
         if ($mailingInfo['smtpServer'] == '' || !$mailingInfo['smtpServer']) {
@@ -550,7 +550,7 @@ class CRM_Core_Config extends CRM_Core_Config_Variables {
         // CRM-9349
         $params['persist'] = $persist;
 
-        self::$_mail = Mail::factory('smtp', $params);
+        self::$_mail = self::_createMailer('smtp', $params);
       }
       elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SENDMAIL) {
         if ($mailingInfo['sendmail_path'] == '' ||
@@ -562,16 +562,15 @@ class CRM_Core_Config extends CRM_Core_Config_Variables {
         $params['sendmail_path'] = $mailingInfo['sendmail_path'];
         $params['sendmail_args'] = $mailingInfo['sendmail_args'];
 
-        self::$_mail = Mail::factory('sendmail', $params);
+        self::$_mail = self::_createMailer('sendmail', $params);
       }
       elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MAIL) {
-        $params = array();
-        self::$_mail = Mail::factory('mail', $params);
+        self::$_mail = self::_createMailer('mail', array());
       }
       elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) {
-        self::$_mail = Mail::factory('mock', $params);
+        self::$_mail = self::_createMailer('mock', array());
       }
-      elseif ($mailingInfo['outBound_option'] == 2) {
+      elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED) {
         CRM_Core_Error::debug_log_message(ts('Outbound mail has been disabled. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
         CRM_Core_Session::setStatus(ts('Outbound mail has been disabled. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
       }
@@ -584,6 +583,21 @@ class CRM_Core_Config extends CRM_Core_Config_Variables {
     return self::$_mail;
   }
 
+  /**
+   * @param string $driver 'CRM_Mailing_BAO_Spool' or a name suitable for Mail::factory()
+   * @param array $params
+   * @return Mail|NULL
+   */
+  public static function _createMailer($driver, $params) {
+    if ($driver == 'CRM_Mailing_BAO_Spool') {
+      $mailer = new CRM_Mailing_BAO_Spool($params);
+    } else {
+      $mailer = Mail::factory($driver, $params);
+    }
+    CRM_Utils_Hook::hook_civicrm_alterMailer($mailer, $driver, $params);
+    return $mailer;
+  }
+
   /**
    * delete the web server writable directories
    *
diff --git a/CRM/Utils/FakeObject.php b/CRM/Utils/FakeObject.php
new file mode 100644 (file)
index 0000000..68e36bf
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * This is a quick-and-dirty way to define a vaguely-class-ish structure. It's non-performant, abnormal,
+ * and not a complete OOP system. Only use for testing/mocking.
+ *
+ * @code
+ * $object = new CRM_Utils_FakeObject(array(
+ *   'doIt' => function() {  print "It!\n"; }
+ * ));
+ * $object->doIt();
+ * @endcode
+ */
+class CRM_Utils_FakeObject {
+  function __construct($array) {
+    $this->array = $array;
+  }
+
+  function __call($name, $arguments) {
+    if (isset($this->array[$name]) && is_callable($this->array[$name])) {
+      return call_user_func_array($this->array[$name], $arguments);
+    } else {
+      throw new Exception("Call to unimplemented method: $name");
+    }
+  }
+}
\ No newline at end of file
index 646185618ddc83561427d3374874934bd95ff5d3..512f3a24de0142c3965e31f09e8b11f19c75efdb 100644 (file)
@@ -1349,6 +1349,21 @@ abstract class CRM_Utils_Hook {
       self::$_nullObject, 'civicrm_alterBarcode');
   }
 
+  /**
+   * Modify or replace the Mailer object used for outgoing mail.
+   *
+   * @param object $mailer
+   *   The default mailer produced by normal configuration; a PEAR "Mail" class (like those returned by Mail::factory)
+   * @param string $driver
+   *   The type of the default mailer (eg "smtp", "sendmail", "mock", "CRM_Mailing_BAO_Spool")
+   * @param array $params
+   *   The default mailer config options
+   * @see Mail::factory
+   */
+  static function hook_civicrm_alterMailer(&$mailer, $driver, $params) {
+    return self::singleton()->invoke(3, $mailer, $driver, $params, self::$_nullObject, self::$_nullObject, 'civicrm_alterMailer');
+  }
+
   /**
    * This hook is called while building the core search query,
    * so hook implementers can provide their own query objects which alters/extends core search.
diff --git a/tests/phpunit/CRM/Core/Config/MailerTest.php b/tests/phpunit/CRM/Core/Config/MailerTest.php
new file mode 100644 (file)
index 0000000..7365c3a
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+require_once 'CiviTest/CiviUnitTestCase.php';
+
+class CRM_Core_Config_MailerTest extends CiviUnitTestCase {
+
+  /**
+   * @var array (string=>int) Keep count of the #times different functions are called
+   */
+  var $calls;
+
+  function setUp() {
+    $this->calls = array(
+      'civicrm_alterMailer' => 0,
+      'send' => 0,
+    );
+    parent::setUp();
+  }
+
+  function testHookAlterMailer() {
+    $test = $this;
+    $mockMailer = new CRM_Utils_FakeObject(array(
+      'send' => function ($recipients, $headers, $body) use ($test) {
+        $test->calls['send']++;
+        $test->assertEquals(array('to@example.org'), $recipients);
+        $test->assertEquals('Subject Example', $headers['Subject']);
+      }
+    ));
+
+    CRM_Utils_Hook::singleton()->setHook('civicrm_alterMailer',
+      function (&$mailer, $driver, $params) use ($test, $mockMailer) {
+        $test->calls['civicrm_alterMailer']++;
+        $test->assertTrue(is_string($driver) && !empty($driver));
+        $test->assertTrue(is_array($params));
+        $test->assertTrue(is_callable(array($mailer, 'send')));
+        $mailer = $mockMailer;
+      }
+    );
+
+    $params = array();
+    $params['groupName'] = 'CRM_Core_Config_MailerTest';
+    $params['from'] = 'From Example <from@example.com>';
+    $params['toName'] = 'To Example';
+    $params['toEmail'] = 'to@example.org';
+    $params['subject'] = 'Subject Example';
+    $params['text'] = 'Example text';
+    $params['html'] = '<p>Example HTML</p>';
+    CRM_Utils_Mail::send($params);
+
+    $this->assertEquals(1, $this->calls['civicrm_alterMailer']);
+    $this->assertEquals(1, $this->calls['send']);
+
+    // once more, just to make sure the hooks are called right #times
+    CRM_Utils_Mail::send($params);
+    CRM_Utils_Mail::send($params);
+    $this->assertEquals(1, $this->calls['civicrm_alterMailer']);
+    $this->assertEquals(3, $this->calls['send']);
+  }
+
+}
index 5006e9bde6b1ff9f167d1af5eef699eb39aba8ad..d4f1645dd21cd49bbd97e7f67e1a25d963a68352 100644 (file)
@@ -330,6 +330,7 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
     // FIXME: look at it closer in second stage
 
     // initialize the object once db is loaded
+    CRM_Core_Config::$_mail = NULL;
     $config = CRM_Core_Config::singleton();
 
     // when running unit tests, use mockup user framework