CRM-17645 - Add entityRef support for autocompleting Cases
authorColeman Watts <coleman@civicrm.org>
Wed, 13 Jan 2016 04:04:32 +0000 (23:04 -0500)
committerColeman Watts <coleman@civicrm.org>
Thu, 14 Jan 2016 17:48:08 +0000 (12:48 -0500)
CRM/Case/BAO/Case.php
CRM/Case/BAO/CaseContact.php [new file with mode: 0644]
CRM/Case/Form/Activity/OpenCase.php
CRM/Case/Page/AJAX.php
CRM/Core/DAO/permissions.php
api/v3/Case.php
api/v3/CaseContact.php [new file with mode: 0644]
tests/phpunit/CRM/Case/BAO/CaseTest.php

index 9e29f77a6babecae96474de806323b0bf35c2d76..97fd1764ee3ccfdd4f47636209a5d350774ebda4 100644 (file)
@@ -149,66 +149,6 @@ class CRM_Case_BAO_Case extends CRM_Case_DAO_Case {
     return $case;
   }
 
-  /**
-   * Create case contact record.
-   *
-   * @param array $params
-   *   case_id, contact_id
-   *
-   * @return object
-   */
-  public static function addCaseToContact($params) {
-    $caseContact = new CRM_Case_DAO_CaseContact();
-    $caseContact->case_id = $params['case_id'];
-    $caseContact->contact_id = $params['contact_id'];
-    $caseContact->find(TRUE);
-    $caseContact->save();
-
-    // add to recently viewed
-    $caseType = CRM_Case_BAO_Case::getCaseType($caseContact->case_id);
-    $url = CRM_Utils_System::url('civicrm/contact/view/case',
-      "action=view&reset=1&id={$caseContact->case_id}&cid={$caseContact->contact_id}&context=home"
-    );
-
-    $title = CRM_Contact_BAO_Contact::displayName($caseContact->contact_id) . ' - ' . $caseType;
-
-    $recentOther = array();
-    if (CRM_Core_Permission::checkActionPermission('CiviCase', CRM_Core_Action::DELETE)) {
-      $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/case',
-        "action=delete&reset=1&id={$caseContact->case_id}&cid={$caseContact->contact_id}&context=home"
-      );
-    }
-
-    // add the recently created case
-    CRM_Utils_Recent::add($title,
-      $url,
-      $caseContact->case_id,
-      'Case',
-      $params['contact_id'],
-      NULL,
-      $recentOther
-    );
-
-    return $caseContact;
-  }
-
-  /**
-   * Delete case contact record.
-   *
-   * @param int $caseID
-   */
-  public static function deleteCaseContact($caseID) {
-    $caseContact = new CRM_Case_DAO_CaseContact();
-    $caseContact->case_id = $caseID;
-    $caseContact->delete();
-
-    // delete the recently created Case
-    $caseRecent = array(
-      'id' => $caseID,
-      'type' => 'Case',
-    );
-    CRM_Utils_Recent::del($caseRecent);
-  }
 
   /**
    * Convert associative array names to values and vice-versa.
@@ -3426,28 +3366,30 @@ WHERE id IN (' . implode(',', $copiedActivityIds) . ')';
   /**
    * @inheritDoc
    */
-  public function apiWhereClause($tableAlias) {
-    $clauses = array();
-    // Only case admins can view deleted cases
-    if (!CRM_Core_Permission::check('administer CiviCase')) {
-      $clauses[] = "`$tableAlias`.is_deleted = 0";
-    }
+  public function apiWhereClause() {
+    $clauses = array(
+      'id' => array(),
+      // Only case admins can view deleted cases
+      'is_deleted' => CRM_Core_Permission::check('administer CiviCase') ? NULL : "= 0",
+    );
     // Ensure the user has permission to view the case client
-    $contactClause = CRM_Contact_BAO_Contact_Permission::cacheSubquery('contact_id');
-    if ($contactClause !== NULL) {
-      $clauses[] = "`$tableAlias`.id IN (SELECT case_id FROM civicrm_case_contact WHERE $contactClause)";
+    $contactClause = CRM_Contact_BAO_Contact_Permission::cacheSubquery();
+    if ($contactClause) {
+      $contactClause = implode(' AND contact_id ', $contactClause);
+      $clauses['id'][] = "IN (SELECT case_id FROM civicrm_case_contact WHERE contact_id $contactClause)";
     }
     // The api gatekeeper ensures the user has at least "access all cases and activities"
     // so if they do not have permission to see all cases we'll assume they can only access their own
     if (!CRM_Core_Permission::check('access all cases and activities')) {
       $user = (int) CRM_Core_Session::getLoggedInContactID();
-      $clauses[] = "`$tableAlias`.id IN (
+      $clauses['id'][] = "IN (
         SELECT r.case_id FROM civicrm_relationship r, civicrm_case_contact cc WHERE r.is_active = 1 AND cc.case_id = r.case_id AND (
-          (contact_id_a = cc.contact_id AND contact_id_b = $user) OR (contact_id_b = cc.contact_id AND contact_id_a = $user)
+          (r.contact_id_a = cc.contact_id AND r.contact_id_b = $user) OR (r.contact_id_b = cc.contact_id AND r.contact_id_a = $user)
         )
       )";
     }
-    return $clauses ? implode(' AND ', $clauses) : NULL;
+
+    return $clauses;
   }
 
 }
diff --git a/CRM/Case/BAO/CaseContact.php b/CRM/Case/BAO/CaseContact.php
new file mode 100644 (file)
index 0000000..4b4bd06
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2015
+ */
+
+/**
+ * This class contains the functions for Case Contact management.
+ */
+class CRM_Case_BAO_CaseContact extends CRM_Case_DAO_CaseContact {
+
+  /**
+   * Create case contact record.
+   *
+   * @param array $params
+   *   case_id, contact_id
+   *
+   * @return CRM_Case_BAO_CaseContact
+   */
+  public static function create($params) {
+    $caseContact = new self();
+    $caseContact->copyValues($params);
+    $caseContact->save();
+
+    // add to recently viewed
+    $caseType = CRM_Case_BAO_Case::getCaseType($caseContact->case_id);
+    $url = CRM_Utils_System::url('civicrm/contact/view/case',
+      "action=view&reset=1&id={$caseContact->case_id}&cid={$caseContact->contact_id}&context=home"
+    );
+
+    $title = CRM_Contact_BAO_Contact::displayName($caseContact->contact_id) . ' - ' . $caseType;
+
+    $recentOther = array();
+    if (CRM_Core_Permission::checkActionPermission('CiviCase', CRM_Core_Action::DELETE)) {
+      $recentOther['deleteUrl'] = CRM_Utils_System::url('civicrm/contact/view/case',
+        "action=delete&reset=1&id={$caseContact->case_id}&cid={$caseContact->contact_id}&context=home"
+      );
+    }
+
+    // add the recently created case
+    CRM_Utils_Recent::add($title,
+      $url,
+      $caseContact->case_id,
+      'Case',
+      $caseContact->contact_id,
+      NULL,
+      $recentOther
+    );
+
+    return $caseContact;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function apiWhereClause() {
+    // In order to make things easier for downstream developers, we reuse and adapt case acls here.
+    // This doesn't yield the most straightforward query, but hopefully the sql engine will sort it out.
+    $clauses = array(
+      // Case acls already check for contact access so we can just mark contact_id as handled
+      'contact_id' => NULL,
+      'case_id' => array(),
+    );
+    $caseSubclauses = array();
+    $caseBao = new CRM_Case_BAO_Case();
+    foreach ($caseBao->apiWhereClause() as $field => $fieldClauses) {
+      if ($field == 'id') {
+        $clauses['case_id'] = array_merge($clauses['case_id'], (array) $fieldClauses);
+      }
+      else {
+        $caseSubclauses[] = "$field " . implode(" AND $field ", (array) $fieldClauses);
+      }
+    }
+    if ($caseSubclauses) {
+      $clauses['case_id'][] = 'IN (SELECT id FROM civicrm_case WHERE ' . implode(' AND ', $caseSubclauses) . ')';
+    }
+    return $clauses;
+  }
+
+}
index 2bb75669ed6bea619158295663cbfa05153286b9..b6e32b03ca24129504dc023f50e2daa57683b182 100644 (file)
@@ -303,7 +303,7 @@ class CRM_Case_Form_Activity_OpenCase {
           'case_id' => $params['case_id'],
           'contact_id' => $cliId,
         );
-        CRM_Case_BAO_Case::addCaseToContact($contactParams);
+        CRM_Case_BAO_CaseContact::create($contactParams);
       }
     }
     else {
@@ -311,7 +311,7 @@ class CRM_Case_Form_Activity_OpenCase {
         'case_id' => $params['case_id'],
         'contact_id' => $form->_currentlyViewedContactId,
       );
-      CRM_Case_BAO_Case::addCaseToContact($contactParams);
+      CRM_Case_BAO_CaseContact::create($contactParams);
     }
 
     // 2. initiate xml processor
index 9dbc11a69ecdcb8b67d13051295b0c76358fa22b..874c2aa1357163e377502d0ef38ba519f5f7bac4 100644 (file)
@@ -167,7 +167,7 @@ class CRM_Case_Page_AJAX {
       'contact_id' => $contactId,
     );
 
-    CRM_Case_BAO_Case::addCaseToContact($params);
+    CRM_Case_BAO_CaseContact::create($params);
 
     // add case relationships
     CRM_Case_BAO_Case::addCaseRelationships($caseId, $contactId);
index bc2634d0abab80bd6c5f060a66c937bfe7712688..a09cbadbcedb2dedf2235c8f0f60a2e2e6581b3a 100644 (file)
@@ -183,6 +183,7 @@ function _civicrm_api3_permissions($entity, $action, &$params) {
       'access my cases and activities',
     ),
   );
+  $permissions['case_contact'] = $permissions['case'];
 
   // Campaign permissions
   $permissions['campaign'] = array(
index cb57f1537ad05c2f658ff018b56c39cd9cd650b0..acc17ee0425cd93043b516657d058525178a8d51 100644 (file)
@@ -434,3 +434,41 @@ function _civicrm_api3_case_format_params(&$params) {
     $params['case_type'] = $caseTypes[$params['case_type_id']];
   }
 }
+
+/**
+ * It actually works a lot better to use the CaseContact api instead of the Case api
+ * for entityRef fields so we can perform the necessary joins,
+ * so we pass off getlist requests to the CaseContact api.
+ *
+ * @param array $params
+ * @return mixed
+ */
+function civicrm_api3_case_getList($params) {
+  require_once 'api/v3/Generic/Getlist.php';
+  require_once 'api/v3/CaseContact.php';
+  $params['id_field'] = 'case_id';
+  $params['label_field'] = $params['search_field'] = 'contact_id.sort_name';
+  $params['description_field'] = array(
+    'case_id',
+    'case_id.case_type_id.title',
+    'case_id.subject',
+    'case_id.status_id',
+    'case_id.start_date',
+  );
+  $apiRequest = array(
+    'entity' => 'CaseContact',
+    'action' => 'getlist',
+    'params' => $params,
+  );
+  return civicrm_api3_generic_getList($apiRequest);
+}
+
+/**
+ * Needed due to the above override
+ * @param $params
+ * @param $apiRequest
+ */
+function _civicrm_api3_case_getlist_spec(&$params, $apiRequest) {
+  require_once 'api/v3/Generic/Getlist.php';
+  _civicrm_api3_generic_getlist_spec($params, $apiRequest);
+}
diff --git a/api/v3/CaseContact.php b/api/v3/CaseContact.php
new file mode 100644 (file)
index 0000000..e24cabb
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * This api exposes CiviCRM CaseContact records.
+ *
+ * @package CiviCRM_APIv3
+ */
+
+/**
+ * Save a CaseContact.
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+function civicrm_api3_case_contact_create($params) {
+  return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params);
+}
+
+/**
+ * Get a CaseContact.
+ *
+ * @param array $params
+ *
+ * @return array
+ *   Array of retrieved case_contact property values.
+ */
+function civicrm_api3_case_contact_get($params) {
+  return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params);
+}
+
+/**
+ * Delete a CaseContact.
+ *
+ * @param array $params
+ *
+ * @return array
+ *   Array of deleted values.
+ */
+function civicrm_api3_case_contact_delete($params) {
+  return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params);
+}
+
+/**
+ * Results formatting for Case entityRef lookups.
+ *
+ * @param array $result
+ * @param array $request
+ * @param string $entity
+ * @param array $fields
+ *
+ * @return array
+ */
+function _civicrm_api3_case_contact_getlist_output($result, $request, $entity, $fields) {
+  $output = array();
+  if (!empty($result['values'])) {
+    foreach ($result['values'] as $row) {
+      $data = array(
+        'id' => $row[$request['id_field']],
+        'label' => $row[$request['label_field']] . ' - ' . $row['case_id.case_type_id.title'],
+      );
+      $status = CRM_Core_PseudoConstant::getLabel('CRM_Case_BAO_Case', 'status_id', $row['case_id.status_id']);
+      $date = CRM_Utils_Date::customFormat($row['case_id.start_date']);
+      $data['description'] = array(
+        "#{$row['case_id']}: $status " . ts('(opened %1)', array(1 => $date)),
+        $row['case_id.subject'],
+      );
+      if (!empty($request['image_field'])) {
+        $data['image'] = isset($row[$request['image_field']]) ? $row[$request['image_field']] : '';
+      }
+      $output[] = $data;
+    }
+  }
+  return $output;
+}
index a45f2fc5aa034a220e3c28efa97d8856ec5329b9..3a2addb01f78f4396370d28058e2d5e2266000c6 100644 (file)
@@ -41,7 +41,7 @@ class CRM_Case_BAO_CaseTest extends CiviUnitTestCase {
       'case_id' => 1,
       'contact_id' => 17,
     );
-    CRM_Case_BAO_Case::addCaseToContact($params);
+    CRM_Case_BAO_CaseContact::create($params);
 
     $recent = CRM_Utils_Recent::get();
     $this->assertEquals('Test Contact - Housing Support', $recent[0]['title']);