Merge pull request #2257 from kurund/CRM-14004
[civicrm-core.git] / CRM / Contact / BAO / Query.php
index 2be27ce642484eedc209a5cddab3571e14e0a775..f875fcee64551f70f5fbf5c9d4d9b4d0df31018a 100644 (file)
@@ -635,6 +635,12 @@ class CRM_Contact_BAO_Query {
         }
       }
 
+      if (in_array($name, array('prefix_id', 'suffix_id', 'gender_id'))) {
+        if (CRM_Utils_Array::value($field['pseudoconstant']['optionGroupName'], $this->_returnProperties)) {
+          $makeException = TRUE;
+        }
+      }
+
       $cfID = CRM_Core_BAO_CustomField::getKeyID($name);
       if (
         CRM_Utils_Array::value($name, $this->_paramLookup) ||
@@ -1526,6 +1532,7 @@ class CRM_Contact_BAO_Query {
       case 'group':
         $this->group($values);
         return;
+
       case 'group_type':
         // so we resolve this into a list of groups & proceed as if they had been
         // handed in
@@ -1537,8 +1544,8 @@ class CRM_Contact_BAO_Query {
         $this->_paramLookup['group'][0][2] = $values[2] = $this->getGroupsFromTypeCriteria($value);
         $this->group($values);
         return;
-      // case tag comes from find contacts
 
+      // case tag comes from find contacts
       case 'tag_search':
         $this->tagSearch($values);
         return;
@@ -1625,6 +1632,7 @@ class CRM_Contact_BAO_Query {
       case 'activity_campaign_id':
       case 'activity_engagement_level':
       case 'activity_id':
+      case 'activity_result':
       case 'source_contact':
         CRM_Activity_BAO_Query::whereClauseSingle($values, $this);
         return;
@@ -1833,8 +1841,8 @@ class CRM_Contact_BAO_Query {
     $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
 
     if (substr($name, 0, 14) === 'state_province') {
-      $setTables = FALSE;
       if (isset($locType[1]) && is_numeric($locType[1])) {
+        $setTables = FALSE;
         $aName = "{$locationType[$locType[1]]}-address";
         $where = "`$aName`.state_province_id";
       }
@@ -1872,8 +1880,8 @@ class CRM_Contact_BAO_Query {
       }
     }
     elseif (substr($name, 0, 7) === 'country') {
-      $setTables = FALSE;
       if (isset($locType[1]) && is_numeric($locType[1])) {
+        $setTables = FALSE;
         $aName = "{$locationType[$locType[1]]}-address";
         $where = "`$aName`.country_id";
       }
@@ -1890,6 +1898,7 @@ class CRM_Contact_BAO_Query {
         $intVal = CRM_Utils_Array::key($value, $countries);
         $this->_where[$grouping][] = self::buildClause($where, $op, $intVal, 'Positive');
       }
+
       if (!$lType) {
         $this->_qill[$grouping][] = ts('Country') . " $op '$value'";
       }
@@ -1898,8 +1907,8 @@ class CRM_Contact_BAO_Query {
       }
     }
     elseif (substr($name, 0, 6) === 'county') {
-      $setTables = FALSE;
       if (isset($locType[1]) && is_numeric($locType[1])) {
+        $setTables = FALSE;
         $aName = "{$locationType[$locType[1]]}-address";
         $where = "`$aName`.county_id";
       }
@@ -1985,9 +1994,11 @@ class CRM_Contact_BAO_Query {
         $op = 'LIKE';
       }
       $wc = self::caseImportant($op) ? "LOWER(contact_a.organization_name)" : "contact_a.organization_name";
-      $this->_where[$grouping][] = self::buildClause($wc, $op,
-        "'$value' AND contact_a.contact_type ='Individual'"
+      $ceWhereClause = self::buildClause($wc, $op,
+        $value
       );
+      $ceWhereClause .= " AND contact_a.contact_type = 'Individual'";
+      $this->_where[$grouping][] = $ceWhereClause;
       $this->_qill[$grouping][] = "$field[title] $op \"$value\"";
     }
     elseif ($name === 'email_greeting') {
@@ -2027,7 +2038,7 @@ class CRM_Contact_BAO_Query {
       }
 
       $wc = 'civicrm_website.url';
-      $this->_where[$grouping][] = self::buildClause($wc, $op, "'$value'");
+      $this->_where[$grouping][] = $d = self::buildClause($wc, $op, $value);
       $this->_qill[$grouping][] = "$field[title] $op \"$value\"";
     }
     elseif ($name === 'contact_is_deleted') {
@@ -2636,7 +2647,7 @@ class CRM_Contact_BAO_Query {
 
     if (!$skipGroup) {
       $gcTable = "`civicrm_group_contact-{$groupIds}`";
-      $this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON contact_a.id = {$gcTable}.contact_id ";
+      $this->_tables[$gcTable] = $this->_whereTables[$gcTable] = " LEFT JOIN civicrm_group_contact {$gcTable} ON ( contact_a.id = {$gcTable}.contact_id AND {$gcTable}.group_id $op ( $groupIds ) )";
     }
 
     $qill = ts('Contacts %1', array(1 => $op));
@@ -3181,11 +3192,15 @@ WHERE  id IN ( $groupIDs )
    */
   function phone_numeric(&$values) {
     list($name, $op, $value, $grouping, $wildcard) = $values;
-    // Strip non-numeric characters
-    $number = preg_replace('/[^\d]/', '', $value);
+    // Strip non-numeric characters; allow wildcards
+    $number = preg_replace('/[^\d%]/', '', $value);
     if ($number) {
+      if ( strpos($number, '%') === FALSE ) {
+        $number = "%$number%";
+      }
+
       $this->_qill[$grouping][] = ts('Phone number contains') . " $number";
-      $this->_where[$grouping][] = self::buildClause('civicrm_phone.phone_numeric', 'LIKE', "%$number%", 'String');
+      $this->_where[$grouping][] = self::buildClause('civicrm_phone.phone_numeric', 'LIKE', "$number", 'String');
       $this->_tables['civicrm_phone'] = $this->_whereTables['civicrm_phone'] = 1;
     }
   }
@@ -3736,7 +3751,10 @@ WHERE  id IN ( $groupIDs )
     list($name, $op, $value, $grouping, $wildcard) = $values;
 
     $pref = array();
-    if (!is_array($value)) {
+    if (in_array($op, array( 'IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) {
+      $value = NULL;
+    }
+    elseif (!is_array($value)) {
       $v = array();
       $value = trim($value, ' ()');
       if (strpos($value, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
@@ -3763,10 +3781,16 @@ WHERE  id IN ( $groupIDs )
     $commPref = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
 
     $sqlValue = array();
+    $showValue = array();
     $sql = "contact_a.preferred_communication_method";
-    foreach ($pref as $val) {
-      $sqlValue[] = "( $sql like '%" . CRM_Core_DAO::VALUE_SEPARATOR . $val . CRM_Core_DAO::VALUE_SEPARATOR . "%' ) ";
-      $showValue[] = $commPref[$val];
+    if (in_array($op, array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) {
+      $sqlValue[] = "{$sql} {$op}";
+    }
+    else {
+      foreach ($pref as $val) {
+        $sqlValue[] = "( $sql like '%" . CRM_Core_DAO::VALUE_SEPARATOR . $val . CRM_Core_DAO::VALUE_SEPARATOR . "%' ) ";
+        $showValue[] = $commPref[$val];
+      }
     }
     $this->_where[$grouping][] = "( " . implode(' OR ', $sqlValue) . " )";
     $this->_qill[$grouping][] = ts('Preferred Communication Method') . " $op " . implode(' ' . ts('or') . ' ', $showValue);
@@ -3787,6 +3811,7 @@ WHERE  id IN ( $groupIDs )
     // for relatinship search we always do wildcard
     $targetName = $this->getWhereValues('relation_target_name', $grouping);
     $relStatus = $this->getWhereValues('relation_status', $grouping);
+    $relPermission = $this->getWhereValues('relation_permission', $grouping);
     $targetGroup = $this->getWhereValues('relation_target_group', $grouping);
 
     $nameClause = $name = NULL;
@@ -3817,9 +3842,12 @@ WHERE  id IN ( $groupIDs )
       self::$_relType = 'reciprocal';
     }
     // if we are creating a temp table we build our own where for the relationship table
+    $relationshipTempTable = NULL;
     if(self::$_relType == 'reciprocal' && empty($targetGroup)) {
       $where = array();
-      self::$_relationshipTempTable = $relationshipTempTable = 'civicrm_temp_rel' . rand(0,99999);
+      self::$_relationshipTempTable =
+        $relationshipTempTable =
+        CRM_Core_DAO::createTempTableName( 'civicrm_rel');
       if($nameClause) {
         $where[$grouping][] = " sort_name $nameClause ";
       }
@@ -3849,8 +3877,10 @@ WHERE  id IN ( $groupIDs )
       //add contacts from static groups
       $this->_tables['civicrm_relationship_group_contact'] =
         $this->_whereTables['civicrm_relationship_group_contact'] =
-        " LEFT JOIN civicrm_group_contact civicrm_relationship_group_contact ON civicrm_relationship_group_contact.contact_id = contact_b.id";
-      $groupWhere[] = "( civicrm_relationship_group_contact.group_id IN  (" . implode(",", $targetGroup[2]) . ") )";
+        " LEFT JOIN civicrm_group_contact civicrm_relationship_group_contact ON civicrm_relationship_group_contact.contact_id = contact_b.id AND civicrm_relationship_group_contact.status = 'Added'";
+      $groupWhere[] =
+        "( civicrm_relationship_group_contact.group_id IN  (" .
+        implode(",", $targetGroup[2]) .  ") ) ";
 
       //add contacts from saved searches
       $ssWhere = $this->addGroupContactCache($targetGroup[2], "civicrm_relationship_group_contact_cache", "contact_b");
@@ -3862,7 +3892,7 @@ WHERE  id IN ( $groupIDs )
       $this->_where[$grouping][] = "( " . implode(" OR ", $groupWhere) . " )";
 
       //Get the names of the target groups for the qill
-      $groupNames = &CRM_Core_PseudoConstant::group();
+      $groupNames = CRM_Core_PseudoConstant::group();
       $qillNames = array();
       foreach ($targetGroup[2] as $groupId) {
         if (array_key_exists($groupId, $groupNames)) {
@@ -3892,6 +3922,20 @@ civicrm_relationship.start_date > {$today}
       $this->_qill[$grouping][] = ts('Relationship - Inactive or not Current');
     }
 
+    //check for permissioned, non-permissioned and all permissioned relations
+    if ($relPermission[2] == 1) {
+      $this->_where[$grouping][] = "(
+civicrm_relationship.is_permission_a_b = 1
+)";
+      $this->_qill[$grouping][] = ts('Relationship - Permissioned');
+    } elseif ($relPermission[2] == 2) {
+      //non-allowed permission relationship.
+      $this->_where[$grouping][] = "(
+civicrm_relationship.is_permission_a_b = 0
+)";
+      $this->_qill[$grouping][] = ts('Relationship - Non-permissioned');
+    }
+
     $this->addRelationshipDateClauses($grouping, $where);
     if(!empty($rType) && isset($rType->id)){
       $where[$grouping][] = 'civicrm_relationship.relationship_type_id = ' . $rType->id;
@@ -4132,9 +4176,12 @@ civicrm_relationship.start_date > {$today}
       $sql .= ' GROUP BY contact_a.id';
     }
     if (!empty($sort)) {
+      $sort = CRM_Utils_Type::escape($sort, 'String');
       $sql .= " ORDER BY $sort ";
     }
     if ($row_count > 0 && $offset >= 0) {
+      $offset = CRM_Utils_Type::escape($offset, 'Int');
+      $rowCount = CRM_Utils_Type::escape($row_count, 'Int');
       $sql .= " LIMIT $offset, $row_count ";
     }
 
@@ -4151,7 +4198,7 @@ civicrm_relationship.start_date > {$today}
       $convertedVals = $query->convertToPseudoNames($dao, TRUE);
 
       if (!empty($convertedVals)) {
-        $val = array_merge_recursive($val, $convertedVals);
+        $val = array_replace_recursive($val, $convertedVals);
       }
       $values[$dao->contact_id] = $val;
     }
@@ -4241,9 +4288,11 @@ civicrm_relationship.start_date > {$today}
               $orderBy = str_replace('sort_name', 'contact_a.sort_name', $orderBy);
             }
 
+            $orderBy = CRM_Utils_Type::escape($orderBy, 'String');
             $order = " ORDER BY $orderBy";
 
             if ($sortOrder) {
+              $sortOrder = CRM_Utils_Type::escape($sortOrder, 'String');
               $order .= " $sortOrder";
             }
 
@@ -4293,6 +4342,8 @@ civicrm_relationship.start_date > {$today}
       }
 
       if ($rowCount > 0 && $offset >= 0) {
+        $offset = CRM_Utils_Type::escape($offset, 'Int');
+        $rowCount = CRM_Utils_Type::escape($rowCount, 'Int');
         $limit = " LIMIT $offset, $rowCount ";
       }
     }
@@ -4536,13 +4587,13 @@ SELECT COUNT( civicrm_contribution.total_amount ) as cancel_count,
         'first_name' => 1,
         'middle_name' => 1,
         'last_name' => 1,
-        'individual_prefix' => 1,
-        'individual_suffix' => 1,
+        'prefix_id' => 1,
+        'suffix_id' => 1,
         'email_greeting' => 1,
         'postal_greeting' => 1,
         'addressee' => 1,
         'birth_date' => 1,
-        'gender' => 1,
+        'gender_id' => 1,
         'preferred_communication_method' => 1,
         'do_not_phone' => 1,
         'do_not_email' => 1,
@@ -4843,7 +4894,6 @@ SELECT COUNT( civicrm_contribution.total_amount ) as cancel_count,
         }
 
         $value = CRM_Utils_Type::escape($value, $dataType);
-
         // if we dont have a dataType we should assume
         if ($dataType == 'String' || $dataType == 'Text') {
           $value = "'" . strtolower($value) . "'";
@@ -4872,6 +4922,7 @@ SELECT COUNT( civicrm_contribution.total_amount ) as cancel_count,
       ts('Change Log') => 'civicrm_log',
       ts('Mailings') => 'civicrm_mailing_event_queue',
     );
+    CRM_Contact_BAO_Query_Hook::singleton()->getPanesMapper($panesMapper);
 
     foreach (array_keys($this->_whereTables) as $table) {
       if ($panName = array_search($table, $panesMapper)) {
@@ -5123,7 +5174,6 @@ AND   displayRelType.is_active = 1
 
       if (property_exists($dao, $value['idCol'])) {
         $val = $dao->$value['idCol'];
-        $idColumn = $key;
 
         if (CRM_Utils_System::isNull($val)) {
           $dao->$key = NULL;
@@ -5131,9 +5181,6 @@ AND   displayRelType.is_active = 1
         elseif ($baoName = CRM_Utils_Array::value('bao', $value, NULL)) {
           //preserve id value
           $idColumn = "{$key}_id";
-          if (!empty($this->_fields[$key]['name'])) {
-            $idColumn = $this->_fields[$key]['name'];
-          }
           $dao->$idColumn = $val;
           $dao->$key = CRM_Core_PseudoConstant::getLabel($baoName, $value['pseudoField'], $val);
         }
@@ -5160,7 +5207,6 @@ AND   displayRelType.is_active = 1
             $current[$lastElement] = $dao->$key;
           }
           else {
-            $values[$idColumn] = $dao->$idColumn;
             $values[$key] = $dao->$key;
           }
         }