CRM-17645 - Introduce aclWhere clause into case search
authorColeman Watts <coleman@civicrm.org>
Fri, 15 Jan 2016 01:16:41 +0000 (20:16 -0500)
committerColeman Watts <coleman@civicrm.org>
Fri, 15 Jan 2016 04:56:50 +0000 (23:56 -0500)
CRM/Case/BAO/Query.php
CRM/Case/Form/Search.php
CRM/Core/DAO.php
Civi/API/SelectQuery.php

index 7e721863626ab45da4ea0c0ff604c803dec9b9e6..cbfb04b1e717146433b0df5e450308142e605dcc 100644 (file)
@@ -232,6 +232,11 @@ class CRM_Case_BAO_Query {
         self::whereClauseSingle($query->_params[$id], $query);
       }
     }
+    // Add acl clause
+    $aclClauses = array_filter(CRM_Case_BAO_Case::getAclClause());
+    foreach ($aclClauses as $clause) {
+      $query->_where[0][] = $clause;
+    }
   }
 
   /**
@@ -702,7 +707,7 @@ case_relation_type.id = case_relationship.relationship_type_id )";
     $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_case');
     CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_case', NULL, TRUE, FALSE);
 
-    if (CRM_Core_Permission::check('administer CiviCRM')) {
+    if (CRM_Core_Permission::check('administer CiviCase')) {
       $form->addElement('checkbox', 'case_deleted', ts('Deleted Cases'));
     }
 
index 8c6b2acd31e636698370aaa20de25c69de6e4a7b..1fdc9a265ee83eae7a65bd9f98f6f2655dd239a6 100644 (file)
@@ -245,11 +245,6 @@ class CRM_Case_Form_Search extends CRM_Core_Form_Search {
       }
     }
 
-    //only fetch own cases.
-    if (!CRM_Core_Permission::check('access all cases and activities')) {
-      $this->_formValues['case_owner'] = 2;
-    }
-
     if (empty($this->_formValues['case_deleted'])) {
       $this->_formValues['case_deleted'] = 0;
     }
index e001bcb025446d0f19f75ef0e25568022f7c0e5a..abc3eecc0d085c1789e60df4897bf2b171f05bca 100644 (file)
@@ -2477,4 +2477,23 @@ SELECT contact_id
     return array();
   }
 
+  /**
+   * @param string $tableAlias
+   * @return array
+   */
+  public static function getAclClause($tableAlias = NULL) {
+    $bao = new static();
+    if ($tableAlias === NULL) {
+      $tableAlias = $bao->tableName();
+    }
+    $clauses = array();
+    foreach ((array) $bao->apiWhereClause() as $field => $vals) {
+      $clauses[$field] = NULL;
+      if ($vals) {
+        $clauses[$field] = "`$tableAlias`.`$field` " . implode(" AND `$tableAlias`.`$field` ", (array) $vals);
+      }
+    }
+    return $clauses;
+  }
+
 }
index a6bacb77e0db8abbeced24305aa42078bc0039c4..ceea6f5a1d6eecb391c482336b91a15f0995e32b 100644 (file)
@@ -42,12 +42,10 @@ use Civi\API\Exception\UnauthorizedException;
  */
 class SelectQuery {
 
-  const MAX_JOINS = 4;
+  const
+    MAX_JOINS = 4,
+    MAIN_TABLE_ALIAS = 'a';
 
-  /**
-   * @var \CRM_Core_DAO
-   */
-  protected $bao;
   /**
    * @var string
    */
@@ -86,31 +84,32 @@ class SelectQuery {
   protected $checkPermissions;
 
   /**
-   * @param string $bao_name
+   * @param string $baoName
    *   Name of BAO
    * @param array $params
    *   As passed into api get function.
    * @param bool $isFillUniqueFields
    *   Do we need to ensure unique fields continue to be populated for this api? (backward compatibility).
    */
-  public function __construct($bao_name, $params, $isFillUniqueFields) {
-    $this->bao = new $bao_name();
-    $this->entity = _civicrm_api_get_entity_name_from_dao($this->bao);
+  public function __construct($baoName, $params, $isFillUniqueFields) {
+    $bao = new $baoName();
+    $this->entity = _civicrm_api_get_entity_name_from_dao($bao);
     $this->params = $params;
     $this->isFillUniqueFields = $isFillUniqueFields;
     $this->checkPermissions = \CRM_Utils_Array::value('check_permissions', $this->params, FALSE);
     $this->options = _civicrm_api3_get_options_from_params($this->params);
 
-    $this->entityFieldNames = _civicrm_api3_field_names(_civicrm_api3_build_fields_array($this->bao));
+    $this->entityFieldNames = _civicrm_api3_field_names(_civicrm_api3_build_fields_array($bao));
     // Call this function directly instead of using the api wrapper to force unique field names off
     require_once 'api/v3/Generic.php';
     $apiSpec = \civicrm_api3_generic_getfields(array('entity' => $this->entity, 'version' => 3, 'params' => array('action' => 'get')), FALSE);
     $this->apiFieldSpec = $apiSpec['values'];
 
-    $this->query = \CRM_Utils_SQL_Select::from($this->bao->tableName() . " a");
+    $this->query = \CRM_Utils_SQL_Select::from($bao->tableName() . ' ' . self::MAIN_TABLE_ALIAS);
+    $bao->free();
 
-    // Add ACLs
-    $this->query->where($this->getAclClause('a'));
+    // Add ACLs first to avoid redundant subclauses
+    $this->query->where($this->getAclClause(self::MAIN_TABLE_ALIAS, $baoName));
   }
 
   /**
@@ -134,8 +133,7 @@ class SelectQuery {
       if ($include) {
         $field = $this->getField($field_name);
         if ($field && in_array($field['name'], $this->entityFieldNames)) {
-          // 'a.' is an alias for the entity table.
-          $select_fields["a.{$field['name']}"] = $field['name'];
+          $select_fields[self::MAIN_TABLE_ALIAS . ".{$field['name']}"] = $field['name'];
         }
         elseif ($include && strpos($field_name, '.')) {
           $fkField = $this->addFkField($field_name);
@@ -174,7 +172,7 @@ class SelectQuery {
       }
     }
     // Always select the ID.
-    $select_fields["a.id"] = "id";
+    $select_fields[self::MAIN_TABLE_ALIAS . ".id"] = "id";
 
     // populate where_clauses
     foreach ($this->params as $key => $value) {
@@ -203,14 +201,13 @@ class SelectQuery {
           }
 
           if ($filterKey == 'is_current' || $filterKey == 'isCurrent') {
-            // Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the
-            // concept.
+            // Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the concept.
             $todayStart = date('Ymd000000', strtotime('now'));
             $todayEnd = date('Ymd235959', strtotime('now'));
-            $this->query->where(array("(a.start_date <= '$todayStart' OR a.start_date IS NULL) AND (a.end_date >= '$todayEnd' OR
-          a.end_date IS NULL)
-          AND a.is_active = 1
-        "));
+            $a = self::MAIN_TABLE_ALIAS;
+            $this->query->where("($a.start_date <= '$todayStart' OR $a.start_date IS NULL)
+              AND ($a.end_date >= '$todayEnd' OR $a.end_date IS NULL)
+              AND a.is_active = 1");
           }
         }
       }
@@ -226,7 +223,7 @@ class SelectQuery {
         $key = $field['name'];
       }
       if (in_array($key, $this->entityFieldNames)) {
-        $table_name = 'a';
+        $table_name = self::MAIN_TABLE_ALIAS;
         $column_name = $key;
       }
       elseif (($cf_id = \CRM_Core_BAO_CustomField::getKeyID($key)) != FALSE) {
@@ -285,8 +282,6 @@ class SelectQuery {
       $this->query->limit($this->options['limit'], $this->options['offset']);
     }
 
-    $this->bao->free();
-
     $result_entities = array();
     $result_dao = \CRM_Core_DAO::executeQuery($this->query->toSQL());
 
@@ -344,7 +339,7 @@ class SelectQuery {
     if (count($stack) < 2) {
       return NULL;
     }
-    $prev = 'a';
+    $prev = self::MAIN_TABLE_ALIAS;
     foreach ($stack as $depth => $fieldName) {
       // Setup variables then skip the first level
       if (!$depth) {
@@ -385,7 +380,7 @@ class SelectQuery {
       $joinClause = "LEFT JOIN $fkTable $tableAlias ON $prev.$fk = $tableAlias.id";
 
       // Add acl condition
-      $joinCondition = $this->getAclClause($tableAlias, $fkField['FKClassName'], $subStack);
+      $joinCondition = $this->getAclClause($tableAlias, \_civicrm_api3_get_BAO($fkField['FKApiName']), $subStack);
       if ($joinCondition !== NULL) {
         $joinClause .= " AND $joinCondition";
       }
@@ -414,7 +409,7 @@ class SelectQuery {
    * @return array
    *   Returns the table and field name for adding this field to a SELECT or WHERE clause
    */
-  private function addCustomField($customField, $baseTable = 'a') {
+  private function addCustomField($customField, $baseTable = self::MAIN_TABLE_ALIAS) {
     $tableName = $customField["table_name"];
     $columnName = $customField["column_name"];
     $tableAlias = "{$baseTable}_to_$tableName";
@@ -509,37 +504,27 @@ class SelectQuery {
    * Get acl clause for an entity
    *
    * @param string $tableAlias
-   * @param string $daoName
+   * @param string $baoName
    * @param array $stack
    * @return null|string
    */
-  private function getAclClause($tableAlias, $daoName = NULL, $stack = array()) {
+  private function getAclClause($tableAlias, $baoName, $stack = array()) {
     if (!$this->checkPermissions) {
       return NULL;
     }
-    if (!$daoName) {
-      $bao = $this->bao;
-    }
-    else {
-      $baoName = str_replace('_DAO_', '_BAO_', $daoName);
-      $bao = class_exists($baoName) ? new $baoName() : new $daoName();
-    }
     // Prevent (most) redundant acl sub clauses if they have already been applied to the main entity.
     // FIXME: Currently this only works 1 level deep, but tracking through multiple joins would increase complexity
     // and just doing it for the first join takes care of most acl clause deduping.
     if (count($stack) === 1 && in_array($stack[0], $this->aclFields)) {
       return NULL;
     }
-    $clauses = array();
-    foreach ((array) $bao->apiWhereClause() as $field => $vals) {
-      if (!$stack) {
-        // Track field clauses added to the main entity
-        $this->aclFields[] = $field;
-      }
-      if ($vals) {
-        $clauses[] = "`$tableAlias`.`$field` " . implode(" AND `$tableAlias`.`$field` ", (array) $vals);
-      }
+    $clauses = $baoName::getAclClause($tableAlias);
+    if (!$stack) {
+      // Track field clauses added to the main entity
+      $this->aclFields = array_keys($clauses);
     }
+
+    $clauses = array_filter($clauses);
     return $clauses ? implode(' AND ', $clauses) : NULL;
   }
 
@@ -564,7 +549,7 @@ class SelectQuery {
         $direction = strtoupper(\CRM_Utils_Array::value(1, $words, '')) == 'DESC' ? ' DESC' : '';
         $field = $this->getField($words[0]);
         if ($field) {
-          $orderBy[] = 'a.' . $field['name'] . $direction;
+          $orderBy[] = self::MAIN_TABLE_ALIAS . '.' . $field['name'] . $direction;
         }
         elseif (strpos($words[0], '.')) {
           $join = $this->addFkField($words[0]);