From fddd185d4598838c331106b5a5687eca9d2f4963 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 10 Nov 2014 13:09:29 -0800 Subject: [PATCH] CRM-15578 - Mailing API - Add "preview_recipients" action --- CRM/Mailing/BAO/Mailing.php | 2 +- api/v3/Mailing.php | 61 +++++++++++++++++++++++++--- tests/phpunit/api/v3/MailingTest.php | 42 ++++++++++++++++++- 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/CRM/Mailing/BAO/Mailing.php b/CRM/Mailing/BAO/Mailing.php index 09cfd0e076..cff316f037 100644 --- a/CRM/Mailing/BAO/Mailing.php +++ b/CRM/Mailing/BAO/Mailing.php @@ -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; diff --git a/api/v3/Mailing.php b/api/v3/Mailing.php index 720da93588..bf5f716967 100755 --- a/api/v3/Mailing.php +++ b/api/v3/Mailing.php @@ -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', diff --git a/tests/phpunit/api/v3/MailingTest.php b/tests/phpunit/api/v3/MailingTest.php index 1bb3a0e78d..01f31a6f18 100755 --- a/tests/phpunit/api/v3/MailingTest.php +++ b/tests/phpunit/api/v3/MailingTest.php @@ -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)); -- 2.25.1