CRM-15578 - Mailing API - Move submit() logic from client-side to server-side
authorTim Otten <totten@civicrm.org>
Wed, 17 Dec 2014 03:04:10 +0000 (19:04 -0800)
committerTim Otten <totten@civicrm.org>
Wed, 17 Dec 2014 05:21:02 +0000 (21:21 -0800)
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
js/angular-crmMailing2-services.js
tests/phpunit/api/v3/MailingTest.php

index 91439eaa794ec1663c6a4093849fcf5a3f5086a9..222002d44c890315f7873d29f3897f937394e415 100755 (executable)
  * @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.
  *
index f85798d89db7b1a6d4d84f61e25bdb116f505665..b4e75979a640b8bfce35bc2b474c8027f9f5239b 100644 (file)
@@ -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,
       // @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;
         });
       },
 
index ada2e2c68e44394ae15df59b9679d28091b95e15..bbbb6120531b393bcc22759701e6ae817a9d109e 100755 (executable)
@@ -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 ----------
 
 }