CRM-15578 - Mailing API - Add "preview_recipients" action
authorTim Otten <totten@civicrm.org>
Mon, 10 Nov 2014 21:09:29 +0000 (13:09 -0800)
committerTim Otten <totten@civicrm.org>
Tue, 11 Nov 2014 00:21:17 +0000 (16:21 -0800)
CRM/Mailing/BAO/Mailing.php
api/v3/Mailing.php
tests/phpunit/api/v3/MailingTest.php

index 09cfd0e076146caa55a99159f2483d60037127eb..cff316f0377ee49af1593a82dea4e64d20690963 100644 (file)
@@ -1655,7 +1655,7 @@ ORDER BY   civicrm_email.is_bulkmail DESC
       }
 
       // Populate the recipients.
-      $mailing->getRecipients($job->id, $mailing->id, NULL, NULL, TRUE, FALSE);
+      self::getRecipients($job->id, $mailing->id, NULL, NULL, TRUE, FALSE);
     }
 
     return $mailing;
index 720da93588c399d81a3b708f113c1f5c8bcd9980..bf5f71696708250dd03c3227102bed40143d588e 100755 (executable)
@@ -85,11 +85,10 @@ function civicrm_api3_mailing_get_token($params) {
 function _civicrm_api3_mailing_create_spec(&$params) {
   $params['name']['api.required'] = 1;
   $params['subject']['api.required'] = 1;
-  // should be able to default to 'user_contact_id' & have it work but it didn't work in test so
-  // making required for simplicity
   $params['created_id']['api.required'] = 1;
-  $params['api.mailing_job.create']['api.default'] = 1;
-  $params['api.mailing_job.create']['title'] = 'Schedule Mailing?';
+  $params['created_id']['api.default'] = 'user_contact_id';
+//  $params['api.mailing_job.create']['api.default'] = 1;
+//  $params['api.mailing_job.create']['title'] = 'Schedule Mailing?';
 }
 
 /**
@@ -251,7 +250,7 @@ function civicrm_api3_mailing_event_forward($params) {
  */
 function _civicrm_api3_mailing_event_forward_spec(&$params) {
   $params['job_id']['api.required'] = 1;
-  $params['job_id']['title'] = 'Job ID';
+  $params['g_id']['title'] = 'Job ID';
   $params['event_queue_id']['api.required'] = 1;
   $params['event_queue_id']['title'] = 'Event Queue ID';
   $params['hash']['api.required'] = 1;
@@ -311,6 +310,58 @@ function civicrm_api3_mailing_event_open($params) {
   return civicrm_api3_create_success($params);
 }
 
+/**
+ * Generate a list of likely recipients for a (hypothetical) mailing.
+ *
+ * "Mailing.preview_recipients" == "Mailing.create" + "MailingRecipients.get" + "rollback"
+ *
+ * Ideally, we could add a "force_rollback" option to the API; then downstream code could
+ * combine the actions without needing this function.
+ *
+ * @param array $params
+ * @return array list of recipients
+ * @throws API_Exception
+ */
+function civicrm_api3_mailing_preview_recipients($params) {
+  static $nextId = 0;
+  $savePoint = "civimail_preview_" . ++$nextId;
+  $tx = new CRM_Core_Transaction();
+  CRM_Core_DAO::executeQuery("SAVEPOINT $savePoint");
+
+  // We manually apply defaults (rather than using API defaults) because
+  // (a) these are temporary/non-sense values and (b) we want to
+  // apply defaults regardless of whether mailing has NULL/''/real-value.
+  $params['name'] = 'Placeholder (not saved)';
+  $params['subject'] = 'Placeholder (not saved)';
+  $params['scheduled_date'] = date('YmdHis', time() + 24*60*60);
+
+  // "Mailing.preview_recipients" == "Mailing.create"+"MailingRecipients.get"
+  $params['debug'] = 1;
+  $params['version'] = 3;
+  $params['options']['force_rollback'] = 1;
+  $params['api.MailingRecipients.get'] = array(
+    'options' => array(
+      'limit' => isset($params['options']['limit']) ? $params['options']['limit'] : 1000,
+    ),
+    'api.contact.getvalue' => array(
+      'return' => 'display_name',
+    ),
+    'api.email.getvalue' => array(
+      'return' => 'email',
+    ),
+  );
+
+  try {
+    $mailing = civicrm_api3('Mailing', 'create', $params);
+  } catch (Exception $ex) {
+    CRM_Core_DAO::executeQuery("ROLLBACK TO SAVEPOINT $savePoint");
+    throw $ex;
+  }
+
+  CRM_Core_DAO::executeQuery("ROLLBACK TO SAVEPOINT $savePoint");
+  return civicrm_api3_create_success($mailing['values'][$mailing['id']]['api.MailingRecipients.get']['values']);
+}
+
 function civicrm_api3_mailing_preview($params) {
   civicrm_api3_verify_mandatory($params,
     'CRM_Mailing_DAO_Mailing',
index 1bb3a0e78d8cb1c011b3afe3fb813e86d7079ddc..01f31a6f185b0ad98e0eb94a2d535f2e686ea8b9 100755 (executable)
@@ -37,7 +37,8 @@ class api_v3_MailingTest extends CiviUnitTestCase {
   protected $_apiversion = 3;
   protected $_params = array();
   protected $_entity = 'Mailing';
-
+  protected $_groupIDs; // array(string $pseudonym => int $id)
+  protected $_contactIDs; // array(string $pseudonym => int $id)
 
   /**
    * @return array
@@ -52,7 +53,9 @@ class api_v3_MailingTest extends CiviUnitTestCase {
 
   function setUp() {
     parent::setUp();
+    $this->_contactIDs = array();
     $this->_groupID = $this->groupCreate();
+    $this->_groupIDs = array();
     $this->_email = 'test@test.test';
     $this->_params = array(
       'subject' => 'maild',
@@ -63,7 +66,13 @@ class api_v3_MailingTest extends CiviUnitTestCase {
   }
 
   function tearDown() {
+    foreach ($this->_contactIDs as $contactID) {
+      $this->contactDelete($contactID);
+    }
     $this->groupDelete($this->_groupID);
+    foreach ($this->_groupIDs as $groupID) {
+      $this->groupDelete($groupID);
+    }
   }
 
   /**
@@ -91,6 +100,37 @@ class api_v3_MailingTest extends CiviUnitTestCase {
     $this->deleteMailing($result['id']);
   }
 
+  public function testMailerPreviewRecipients() {
+    // BEGIN SAMPLE DATA
+    $this->groupIDs['inc'] = $this->groupCreate(array('name' => 'Example include group', 'title' => 'Example include group'));
+    $this->groupIDs['exc'] = $this->groupCreate(array('name' => 'Example exclude group', 'title' => 'Example exclude group'));
+    $this->contactIDs['includeme'] = $this->individualCreate(array('include.me@example.org'));
+    $this->contactIDs['excludeme'] = $this->individualCreate(array('exclude.me@example.org'));
+    $this->callAPISuccess('GroupContact', 'create', array('group_id' => $this->groupIDs['inc'], 'contact_id' => $this->contactIDs['includeme']));
+    $this->callAPISuccess('GroupContact', 'create', array('group_id' => $this->groupIDs['inc'], 'contact_id' => $this->contactIDs['excludeme']));
+    $this->callAPISuccess('GroupContact', 'create', array('group_id' => $this->groupIDs['exc'], 'contact_id' => $this->contactIDs['excludeme']));
+
+    $params = $this->_params;
+    $params['groups']['include'] = array($this->groupIDs['inc']);
+    $params['groups']['exclude'] = array($this->groupIDs['exc']);
+    $params['mailings']['include'] = array();
+    $params['mailings']['exclude'] = array();
+    // END SAMPLE DATA
+
+    $maxIDs =  array(
+      'mailing' => CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM civicrm_mailing'),
+      'job' => CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM civicrm_mailing_job'),
+      'group' => CRM_Core_DAO::singleValueQuery('SELECT MAX(id) FROM civicrm_mailing_group'),
+    );
+    $preview = $this->callAPIAndDocument('Mailing', 'preview_recipients', $params, __FUNCTION__, __FILE__);
+    $this->assertDBQuery($maxIDs['mailing'], 'SELECT MAX(id) FROM civicrm_mailing'); // 'Preview should not create any mailing records'
+    $this->assertDBQuery($maxIDs['job'], 'SELECT MAX(id) FROM civicrm_mailing_job'); // 'Preview should not create any mailing_job record'
+    $this->assertDBQuery($maxIDs['group'], 'SELECT MAX(id) FROM civicrm_mailing_group'); // 'Preview should not create any mailing_group records'
+
+    $previewIds = array_values(CRM_Utils_Array::collect('contact_id', $preview['values']));
+    $this->assertEquals(array((string)$this->contactIDs['includeme']), $previewIds);
+  }
+
   public function testMailerSendTestMail() {
     $contactID =  $this->individualCreate();
     $result = $this->callAPISuccess('contact', 'get', array('id' => $contactID));