CRM-21052 - Activity.create API - Abide by setting `civicaseActivityRevisions`
[civicrm-core.git] / api / v3 / Activity.php
index 1457827230576d1ff52f161136d15f1f424cbbe8..924bfe8cb41063105320bf74acfaa5e89d243513 100644 (file)
@@ -43,6 +43,7 @@
  *   API result array
  */
 function civicrm_api3_activity_create($params) {
+  $isNew = empty($params['id']);
 
   if (empty($params['id'])) {
     // an update does not require any mandatory parameters
@@ -91,7 +92,7 @@ function civicrm_api3_activity_create($params) {
   }
   if (!empty($params['case_id'])) {
     $case_id = $params['case_id'];
-    if (!empty($params['id'])) {
+    if (!empty($params['id']) && Civi::settings()->get('civicaseActivityRevisions')) {
       $oldActivityParams = array('id' => $params['id']);
       if (!$oldActivityValues) {
         CRM_Activity_BAO_Activity::retrieve($oldActivityParams, $oldActivityValues);
@@ -158,7 +159,7 @@ function civicrm_api3_activity_create($params) {
   $activityBAO = CRM_Activity_BAO_Activity::create($params);
 
   if (isset($activityBAO->id)) {
-    if ($case_id && !$createRevision) {
+    if ($case_id && $isNew && !$createRevision) {
       // If this is a brand new case activity, add to case(s)
       foreach ((array) $case_id as $singleCaseId) {
         $caseActivityParams = array('activity_id' => $activityBAO->id, 'case_id' => $singleCaseId);
@@ -232,7 +233,7 @@ function _civicrm_api3_activity_get_spec(&$params) {
   $params['tag_id'] = array(
     'title' => 'Tags',
     'description' => 'Find activities with specified tags.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Core_DAO_Tag',
     'FKApiName' => 'Tag',
     'supports_joins' => TRUE,
@@ -240,45 +241,50 @@ function _civicrm_api3_activity_get_spec(&$params) {
   $params['file_id'] = array(
     'title' => 'Attached Files',
     'description' => 'Find activities with attached files.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Core_DAO_File',
     'FKApiName' => 'File',
   );
   $params['case_id'] = array(
     'title' => 'Cases',
     'description' => 'Find activities within specified cases.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Case_DAO_Case',
     'FKApiName' => 'Case',
   );
   $params['contact_id'] = array(
     'title' => 'Activity Contact ID',
     'description' => 'Find activities involving this contact (as target, source, OR assignee).',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Contact_DAO_Contact',
     'FKApiName' => 'Contact',
   );
   $params['target_contact_id'] = array(
     'title' => 'Target Contact ID',
     'description' => 'Find activities with specified target contact.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Contact_DAO_Contact',
     'FKApiName' => 'Contact',
   );
   $params['source_contact_id'] = array(
     'title' => 'Source Contact ID',
     'description' => 'Find activities with specified source contact.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Contact_DAO_Contact',
     'FKApiName' => 'Contact',
   );
   $params['assignee_contact_id'] = array(
     'title' => 'Assignee Contact ID',
     'description' => 'Find activities with specified assignee contact.',
-    'type' => 1,
+    'type' => CRM_Utils_Type::T_INT,
     'FKClassName' => 'CRM_Contact_DAO_Contact',
     'FKApiName' => 'Contact',
   );
+  $params['is_overdue'] = array(
+    'title' => 'Is Activity Overdue',
+    'description' => 'Incomplete activities with a past date.',
+    'type' => CRM_Utils_Type::T_BOOLEAN,
+  );
 }
 
 /**
@@ -295,16 +301,9 @@ function _civicrm_api3_activity_get_spec(&$params) {
  * @throws \Civi\API\Exception\UnauthorizedException
  */
 function civicrm_api3_activity_get($params) {
-
+  $options = _civicrm_api3_get_options_from_params($params, FALSE, 'Activity', 'get');
   $sql = CRM_Utils_SQL_Select::fragment();
-  $recordTypes = civicrm_api3('ActivityContact', 'getoptions', array('field' => 'record_type_id'));
-  $recordTypes = $recordTypes['values'];
-  $activityContactOptions = array(
-    'contact_id' => NULL,
-    'target_contact_id' => array_search('Activity Targets', $recordTypes),
-    'source_contact_id' => array_search('Activity Source', $recordTypes),
-    'assignee_contact_id' => array_search('Activity Assignees', $recordTypes),
-  );
+
   if (empty($params['target_contact_id']) && empty($params['source_contact_id'])
     && empty($params['assignee_contact_id']) &&
     !empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')
@@ -315,6 +314,67 @@ function civicrm_api3_activity_get($params) {
     //$params['contact_id'] = array('IS NOT NULL' => TRUE);
   }
 
+  _civicrm_api3_activity_get_extraFilters($params, $sql);
+
+  // Handle is_overdue sort
+  if (!empty($options['sort'])) {
+    $sort = explode(', ', $options['sort']);
+
+    foreach ($sort as $index => &$sortString) {
+      // Get sort field and direction
+      list($sortField, $dir) = array_pad(explode(' ', $sortString), 2, 'ASC');
+      if ($sortField == 'is_overdue') {
+        $incomplete = implode(',', array_keys(CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::INCOMPLETE)));
+        $sql->orderBy("IF((a.activity_date_time >= NOW() OR a.status_id NOT IN ($incomplete)), 0, 1) $dir", NULL, $index);
+        // Replace the sort with a placeholder which will be ignored by sql
+        $sortString = '(1)';
+      }
+    }
+    $params['options']['sort'] = implode(', ', $sort);
+  }
+
+  // Ensure there's enough data for calculating is_overdue
+  if (!empty($options['return']['is_overdue']) && (empty($options['return']['status_id']) || empty($options['return']['activity_date_time']))) {
+    $options['return']['status_id'] = $options['return']['activity_date_time'] = 1;
+    $params['return'] = array_keys($options['return']);
+  }
+
+  $activities = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, FALSE, 'Activity', $sql);
+  if (!empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')) {
+    // @todo get this to work at the query level - see contact_id join above.
+    foreach ($activities as $activity) {
+      if (!CRM_Activity_BAO_Activity::checkPermission($activity['id'], CRM_Core_Action::VIEW)) {
+        unset($activities[$activity['id']]);
+      }
+    }
+  }
+  if ($options['is_count']) {
+    return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
+  }
+
+  $activities = _civicrm_api3_activity_get_formatResult($params, $activities, $options);
+  //legacy custom data get - so previous formatted response is still returned too
+  return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
+}
+
+/**
+ * Support filters beyond what basic_get can do.
+ *
+ * @param array $params
+ * @param CRM_Utils_SQL_Select $sql
+ * @throws \CiviCRM_API3_Exception
+ * @throws \Exception
+ */
+function _civicrm_api3_activity_get_extraFilters(&$params, &$sql) {
+  // Filter by activity contacts
+  $recordTypes = civicrm_api3('ActivityContact', 'getoptions', array('field' => 'record_type_id'));
+  $recordTypes = $recordTypes['values'];
+  $activityContactOptions = array(
+    'contact_id' => NULL,
+    'target_contact_id' => array_search('Activity Targets', $recordTypes),
+    'source_contact_id' => array_search('Activity Source', $recordTypes),
+    'assignee_contact_id' => array_search('Activity Assignees', $recordTypes),
+  );
   foreach ($activityContactOptions as $activityContactName => $activityContactValue) {
     if (!empty($params[$activityContactName])) {
       if (!is_array($params[$activityContactName])) {
@@ -328,6 +388,19 @@ function civicrm_api3_activity_get($params) {
     }
   }
 
+  // Handle is_overdue filter
+  // Boolean calculated field - does not support operators
+  if (isset($params['is_overdue'])) {
+    $incomplete = implode(',', array_keys(CRM_Activity_BAO_Activity::getStatusesByType(CRM_Activity_BAO_Activity::INCOMPLETE)));
+    if ($params['is_overdue']) {
+      $sql->where('a.activity_date_time < NOW()');
+      $sql->where("a.status_id IN ($incomplete)");
+    }
+    else {
+      $sql->where("(a.activity_date_time >= NOW() OR a.status_id NOT IN ($incomplete))");
+    }
+  }
+
   // Define how to handle filters on some related entities.
   // Subqueries are nice in (a) avoiding duplicates and (b) when the result
   // list is expected to be bite-sized. Joins are nice (a) with larger
@@ -372,23 +445,6 @@ function civicrm_api3_activity_get($params) {
       }
     }
   }
-  $activities = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, FALSE, 'Activity', $sql);
-  if (!empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')) {
-    // @todo get this to work at the query level - see contact_id join above.
-    foreach ($activities as $activity) {
-      if (!CRM_Activity_BAO_Activity::checkPermission($activity['id'], CRM_Core_Action::VIEW)) {
-        unset($activities[$activity['id']]);
-      }
-    }
-  }
-  $options = _civicrm_api3_get_options_from_params($params, FALSE, 'Activity', 'get');
-  if ($options['is_count']) {
-    return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
-  }
-
-  $activities = _civicrm_api3_activity_get_formatResult($params, $activities, $options);
-  //legacy custom data get - so previous formatted response is still returned too
-  return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
 }
 
 /**
@@ -495,6 +551,12 @@ function _civicrm_api3_activity_get_formatResult($params, $activities, $options)
         }
         break;
 
+      case 'is_overdue':
+        foreach ($activities as $key => $activityArray) {
+          $activities[$key]['is_overdue'] = (int) CRM_Activity_BAO_Activity::isOverdue($activityArray);
+        }
+        break;
+
       default:
         if (substr($n, 0, 6) == 'custom') {
           $returnProperties[$n] = $v;