From 6818346ac88265d5e16bad41c96c712b3ca4a89d Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 16 Dec 2014 19:04:10 -0800 Subject: [PATCH] CRM-15578 - Mailing API - Move submit() logic from client-side to server-side This makes it easier to re-use (e.g. submitting the constituents of an A/B test) and easier to secure (e.g. when using CiviMail advanced workflow). --- api/v3/Mailing.php | 57 +++++++++++++++++++++- js/angular-crmMailing2-services.js | 23 +++------ tests/phpunit/api/v3/MailingTest.php | 73 ++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 18 deletions(-) diff --git a/api/v3/Mailing.php b/api/v3/Mailing.php index 91439eaa79..222002d44c 100755 --- a/api/v3/Mailing.php +++ b/api/v3/Mailing.php @@ -50,6 +50,20 @@ * @return array API Success Array */ function civicrm_api3_mailing_create($params, $ids = array()) { + if (CRM_Mailing_Info::workflowEnabled()) { + if (!CRM_Core_Permission::check('create mailings')) { + throw new \Civi\API\Exception\UnauthorizedException("This system uses advanced CiviMail workflows which require additional permissions"); + } + if (!CRM_Core_Permission::check('schedule mailings')) { + unset($params['scheduled_date']); + unset($params['scheduled_id']); + } + if (!CRM_Core_Permission::check('approve mailings')) { + unset($params['approval_date']); + unset($params['approver_id']); + unset($params['approval_status_id']); + } + } return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params); } @@ -103,7 +117,6 @@ function civicrm_api3_mailing_delete($params, $ids = array()) { return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params); } - /** * Handle a get event. * @@ -114,6 +127,48 @@ function civicrm_api3_mailing_get($params) { return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); } +function _civicrm_api3_mailing_submit_spec(&$spec) { + $mailingFields = CRM_Mailing_DAO_Mailing::fields(); + $spec['id'] = $mailingFields['id']; + $spec['scheduled_date'] = $mailingFields['scheduled_date']; + $spec['approval_date'] = $mailingFields['approval_date']; +} + +/** + * @param array $params + */ +function civicrm_api3_mailing_submit($params) { + civicrm_api3_verify_mandatory($params, 'CRM_Mailing_DAO_Mailing', array('id')); + + if (!isset($params['scheduled_date']) && !isset($updateParams['approval_date'])) { + throw new API_Exception("Missing parameter scheduled_date and/or approval_date"); + } + if (!is_numeric(CRM_Core_Session::getLoggedInContactID())) { + throw new API_Exception("Failed to determine current user"); + } + + $updateParams = array(); + $updateParams['id'] = $params['id']; + + // the BAO will autocreate the job + $updateParams['api.mailing_job.create'] = 0; // note: exact match to API default + + // note: we'll pass along scheduling/approval fields, but they may get ignored + // if we don't have permission + if (isset($params['scheduled_date'])) { + $updateParams['scheduled_date'] = $params['scheduled_date']; + $updateParams['scheduled_id'] = CRM_Core_Session::getLoggedInContactID(); + } + if (isset($params['approval_date']) ) { + $updateParams['approval_date'] = $params['approval_date']; + $updateParams['approver_id'] = CRM_Core_Session::getLoggedInContactID(); + $updateParams['approval_status_id'] = CRM_Utils_Array::value('approval_status_id', $updateParams, CRM_Core_OptionGroup::getDefaultValue('mail_approval_status')); + } + + $updateParams['options']['reload'] = 1; + return civicrm_api3('Mailing', 'create', $updateParams); +} + /** * Process a bounce event by passing through to the BAOs. * diff --git a/js/angular-crmMailing2-services.js b/js/angular-crmMailing2-services.js index f85798d89d..b4e75979a6 100644 --- a/js/angular-crmMailing2-services.js +++ b/js/angular-crmMailing2-services.js @@ -21,9 +21,6 @@ return yyyy + "-" + mm + "-" + dd + " " + hh + ":" + min + ":" + sec; }; - // FIXME: Load status ids from DB - var APPROVAL_STATUSES = { Approved: "1", Rejected: "2", None: "3" }; - var crmMailing2 = angular.module('crmMailing2'); // The representation of from/reply-to addresses is inconsistent in the mailing data-model, @@ -299,22 +296,14 @@ // @param mailing Object (per APIv3) // @return Promise submit: function (mailing) { - var changes = { + var params = { + id: mailing.id, approval_date: createNow(), - approver_id: CRM.crmMailing.contactid, - approval_status_id: APPROVAL_STATUSES.Approved, - scheduled_date: mailing.scheduled_date ? mailing.scheduled_date : createNow(), - scheduled_id: CRM.crmMailing.contactid + scheduled_date: mailing.scheduled_date ? mailing.scheduled_date : createNow() }; - var params = _.extend({}, mailing, changes, { - 'api.mailing_job.create': 0 // note: exact match to API default - }); - return crmApi('Mailing', 'create', params).then(function (result) { - if (result.id && !mailing.id) { - mailing.id = result.id; - } // no rollback, so update mailing.id - _.extend(mailing, changes); // Perhaps we should reload mailing based on result? - return result.values[result.id]; + return crmApi('Mailing', 'submit', params).then(function (result) { + _.extend(mailing, result.values[result.id]); // Perhaps we should reload mailing based on result? + return mailing; }); }, diff --git a/tests/phpunit/api/v3/MailingTest.php b/tests/phpunit/api/v3/MailingTest.php index ada2e2c68e..bbbb612053 100755 --- a/tests/phpunit/api/v3/MailingTest.php +++ b/tests/phpunit/api/v3/MailingTest.php @@ -258,6 +258,65 @@ class api_v3_MailingTest extends CiviUnitTestCase { $this->assertEquals(array('alice@example.org', 'bob@example.org', 'carol@example.org'), $deliveredEmails); } + public function submitProvider() { + $cases = array(); // $useLogin, $params, $expectedFailure, $expectedJobCount + $cases[] = array( + TRUE, //useLogin + array('scheduled_date' => '2014-12-13 10:00:00', 'approval_date' => '2014-12-13 00:00:00'), + FALSE, // expectedFailure + 1, // expectedJobCount + ); + $cases[] = array( + FALSE, //useLogin + array('scheduled_date' => '2014-12-13 10:00:00', 'approval_date' => '2014-12-13 00:00:00'), + "/Failed to determine current user/", // expectedFailure + 0, // expectedJobCount + ); + $cases[] = array( + TRUE, //useLogin + array('scheduled_date' => '2014-12-13 10:00:00'), + FALSE, // expectedFailure + 1, // expectedJobCount + ); + $cases[] = array( + TRUE, //useLogin + array(), + "/Missing parameter scheduled_date and.or approval_date/", // expectedFailure + 0, // expectedJobCount + ); + return $cases; + } + + /** + * @param bool $useLogin + * @param array $params + * @param null|string $expectedFailure + * @param int $expectedJobCount + * @dataProvider submitProvider + */ + public function testMailerSubmit($useLogin, $params, $expectedFailure, $expectedJobCount) { + if ($useLogin) { + $this->createLoggedInUser(); + } + + $id = $this->createDraftMailing(); + + $params['id'] = $id; + if ($expectedFailure) { + $submitResult = $this->callAPIFailure('mailing', 'submit', $params); + $this->assertRegExp($expectedFailure, $submitResult['error_message']); + } + else { + $submitResult = $this->callAPIAndDocument('mailing', 'submit', $params, __FUNCTION__, __FILE__); + $this->assertTrue(is_numeric($submitResult['id'])); + $this->assertTrue(is_numeric($submitResult['values'][$id]['scheduled_id'])); + $this->assertEquals($params['scheduled_date'], $submitResult['values'][$id]['scheduled_date']); + } + $this->assertDBQuery($expectedJobCount, 'SELECT count(*) FROM civicrm_mailing_job WHERE mailing_id = %1', array( + 1 => array($id, 'Integer') + )); + } + public function testMailerStats() { $result = $this->groupContactCreate($this->_groupID, 100); $this->assertEquals(100, $result['added']); //verify if 100 contacts are added for group @@ -404,6 +463,20 @@ SELECT event_queue_id, time_stamp FROM mail_{$type}_temp"; ); } + /** + * @return array|int + */ + public function createDraftMailing() { + $createParams = $this->_params; + $createParams['api.mailing_job.create'] = 0; // note: exact match to API default + $createResult = $this->callAPISuccess('mailing', 'create', $createParams, __FUNCTION__, __FILE__); + $this->assertTrue(is_numeric($createResult['id'])); + $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_mailing_job WHERE mailing_id = %1', array( + 1 => array($createResult['id'], 'Integer') + )); + return $createResult['id']; + } + //----------- civicrm_mailing_create ---------- } -- 2.25.1