From: Kurund Jalmi Date: Tue, 23 Dec 2014 19:28:57 +0000 (+0530) Subject: Merge pull request #4772 from jitendrapurohit/CRM-15750 X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=64e59809d10c3ff342718a28f42983ec217dc282;hp=-c;p=civicrm-core.git Merge pull request #4772 from jitendrapurohit/CRM-15750 Fix for CRM-15750 advanced search using relationship and smart group pro... --- 64e59809d10c3ff342718a28f42983ec217dc282 diff --combined CRM/Contact/BAO/Query.php index 01e8afe19c,79d9e99d19..e7760714fb --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@@ -1,7 -1,7 +1,7 @@@ _tables[$tName] = "\nLEFT JOIN $tableName `$tName` ON contact_a.id = `$tName`.contact_id AND `$tName`.$lCond"; + $this->_tables[$tName] = "\nLEFT JOIN $tableName `$tName` ON contact_a.id = `$tName`.contact_id"; + if ($tableName != 'civicrm_phone') { + $this->_tables[$tName] .= " AND `$tName`.$lCond"; + } + elseif (is_numeric($name)) { + $this->_select[$tName] = "IF (`$tName`.is_primary = $name, `$tName`.phone, NULL) as `$tName`"; + } + // this special case to add phone type if ($cond) { $phoneTypeCondition = " AND `$tName`.$cond "; @@@ -1264,7 -1257,7 +1264,7 @@@ } /** - * generate the query based on what type of query we need + * Generate the query based on what type of query we need * * @param boolean $count * @param boolean $sortByChar @@@ -1383,7 -1376,7 +1383,7 @@@ } /** - * @param $name + * @param string $name * @param $grouping * * @return null @@@ -1480,7 -1473,7 +1480,7 @@@ } /** - * @param $id + * @param int $id * @param $values * @param int $wildcard * @param bool $useEquals @@@ -2263,7 -2256,7 +2263,7 @@@ /** * Given a result dao, extract the values and return that array * - * @param Object $dao + * @param CRM_Core_DAO $dao * * @return array values for this query */ @@@ -2308,7 -2301,7 +2308,7 @@@ } /** - * getter for tables array + * Getter for tables array * * @return array * @access public @@@ -2328,7 -2321,7 +2328,7 @@@ } /** - * generate the where clause (used in match contacts and permissions) + * Generate the where clause (used in match contacts and permissions) * * @param array $params * @param array $fields @@@ -2352,7 -2345,7 +2352,7 @@@ } /** - * create the from clause + * Create the from clause * * @param array $tables tables that need to be included in this from clause * if null, return mimimal from clause (i.e. civicrm_contact) @@@ -2620,7 -2613,7 +2620,7 @@@ } /** - * where / qill clause for contact_type + * Where / qill clause for contact_type * * @param $values * @@@ -2688,7 -2681,7 +2688,7 @@@ } /** - * where / qill clause for contact_sub_type + * Where / qill clause for contact_sub_type * * @param $values * @@@ -2697,45 -2690,37 +2697,45 @@@ */ function contactSubType(&$values) { list($name, $op, $value, $grouping, $wildcard) = $values; - $this->includeContactSubTypes($value, $grouping); + $this->includeContactSubTypes($value, $grouping, $op); } /** * @param $value * @param $grouping */ - function includeContactSubTypes($value, $grouping) { + function includeContactSubTypes($value, $grouping, $op = 'LIKE') { $clause = array(); $alias = "contact_a.contact_sub_type"; + $qillOperators = array('NOT LIKE' => ts('Not Like')) + CRM_Core_SelectValues::getSearchBuilderOperators(); - if (is_array($value)) { + $op = str_replace('IN', 'LIKE', $op); + $op = str_replace('=', 'LIKE', $op); + $op = str_replace('!', 'NOT ', $op); + + if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) { + $this->_where[$grouping][] = self::buildClause($alias, $op, $value, 'String'); + } + else if (is_array($value)) { foreach ($value as $k => $v) { if (!empty($k)) { - $clause[$k] = "($alias like '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($k, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; + $clause[$k] = "($alias $op '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($k, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; } } } else { - $clause[$value] = "($alias like '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($value, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; + $clause[$value] = "($alias $op '%" . CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($value, 'String') . CRM_Core_DAO::VALUE_SEPARATOR . "%')"; } if (!empty($clause)) { $this->_where[$grouping][] = "( " . implode(' OR ', $clause) . " )"; - $this->_qill[$grouping][] = ts('Contact Subtype') . ' - ' . implode(' ' . ts('or') . ' ', array_keys($clause)); } + $this->_qill[$grouping][] = ts('Contact Subtype %1 ', array(1 => $qillOperators[$op])) . implode(' ' . ts('or') . ' ', array_keys($clause)); } /** - * where / qill clause for groups + * Where / qill clause for groups * * @param $values * @@@ -2849,7 -2834,7 +2849,7 @@@ } /** - * where / qill clause for smart groups + * Where / qill clause for smart groups * * @param $values * @@@ -2903,19 -2888,19 +2903,19 @@@ WHERE id IN ( $groupIDs $groupIDsFiltered = implode(',', $groupsFiltered); if ($tableAlias == NULL) { - $tableAlias = "civicrm_group_contact_cache_{$groupIDsFiltered}"; + $tableAlias = "`civicrm_group_contact_cache_{$groupIDsFiltered}`"; } - $this->_tables["`{$tableAlias}`"] = $this->_whereTables["`{$tableAlias}`"] = " LEFT JOIN civicrm_group_contact_cache `{$tableAlias}` ON {$joinTable}.id = `{$tableAlias}`.contact_id "; + $this->_tables[$tableAlias] = $this->_whereTables[$tableAlias] = " LEFT JOIN civicrm_group_contact_cache {$tableAlias} ON {$joinTable}.id = {$tableAlias}.contact_id "; - return "`{$tableAlias}`.group_id IN (" . $groupIDsFiltered . ")"; + return "{$tableAlias}.group_id IN (" . $groupIDsFiltered . ")"; } return NULL; } /** - * where / qill clause for cms users + * Where / qill clause for cms users * * @param $values * @@@ -2939,7 -2924,7 +2939,7 @@@ } /** - * all tag search specific + * All tag search specific * * @param $values * @@@ -2993,7 -2978,7 +2993,7 @@@ LEFT JOIN civicrm_tag {$tActTable} ON ( {$etActTable}.tag_id = {$tActTable}.id )"; $this->_where[$grouping][] = "({$tTable}.name $op '". $value . "' OR {$tCaseTable}.name $op '". $value . "' OR {$tActTable}.name $op '". $value . "')"; - $this->_qill[$grouping][] = ts('Tag %1 %2 ', array(1 => $tagTypesText[2], 2 => $op)) . ' ' . $value; + $this->_qill[$grouping][] = ts('Tag %1 %2', array(1 => $tagTypesText[2], 2 => $op)) . ' ' . $value; } else { $etTable = "`civicrm_entity_tag-" . $value . "`"; $tTable = "`civicrm_tag-" . $value . "`"; @@@ -3007,7 -2992,7 +3007,7 @@@ } /** - * where / qill clause for tag + * Where / qill clause for tag * * @param $values * @@@ -3094,7 -3079,7 +3094,7 @@@ } /** - * where/qill clause for notes + * Where/qill clause for notes * * @param $values * @@@ -3142,7 -3127,7 +3142,7 @@@ } /** - * @param $name + * @param string $name * @param $op * @param $grouping * @@@ -3172,7 -3157,7 +3172,7 @@@ } /** - * where / qill clause for sort_name + * Where / qill clause for sort_name * * @param $values * @@@ -3180,16 -3165,17 +3180,16 @@@ * @access public */ function sortName(&$values) { - list($name, $op, $value, $grouping, $wildcard) = $values; + list($fieldName, $op, $value, $grouping, $wildcard) = $values; // handle IS NULL / IS NOT NULL / IS EMPTY / IS NOT EMPTY - if ( $this->nameNullOrEmptyOp( $name, $op, $grouping ) ) { + if ($this->nameNullOrEmptyOp($fieldName, $op, $grouping)) { return; } - $newName = $name; - $name = trim($value); + $input = $value = trim($value); - if (empty($name)) { + if (!strlen($value)) { return; } @@@ -3201,23 -3187,24 +3201,23 @@@ $subGlue = ' OR '; $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower'; - $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); - if (substr($name, 0, 1) == '"' && - substr($name, -1, 1) == '"' - ) { - //If name is encased in double quotes, the value should be taken to be the string in entirety and the - $value = substr($name, 1, -1); - $value = $strtolower(CRM_Core_DAO::escapeString($value)); - $wc = ($newName == 'sort_name') ? 'LOWER(contact_a.sort_name)' : 'LOWER(contact_a.display_name)'; - $sub[] = " ( $wc = '$value' ) "; - if ($config->includeEmailInName) { - $sub[] = " ( civicrm_email.email = '$value' ) "; - } - } - elseif (strpos($name, ',') !== FALSE) { - // if we have a comma in the string, search for the entire string - $value = $strtolower(CRM_Core_DAO::escapeString($name)); - if ($wildcard) { + $firstChar = substr($value, 0, 1); + $lastChar = substr($value, -1, 1); + $quotes = array("'", '"'); + // If string is quoted, strip quotes and otherwise don't alter it + if ((strlen($value) > 2) && in_array($firstChar, $quotes) && in_array($lastChar, $quotes)) { + $value = trim($value, implode('', $quotes)); + } + // Replace spaces with wildcards for a LIKE operation + // UNLESS string contains a comma (this exception is a tiny bit questionable) + elseif ($op == 'LIKE' && strpos($value, ',') === FALSE) { + $value = str_replace(' ', '%', $value); + } + $value = $strtolower(CRM_Core_DAO::escapeString(trim($value))); + if (strlen($value)) { + $fieldsub = array(); + if ($wildcard && $op == 'LIKE') { if ($config->includeWildCardInName) { $value = "'%$value%'"; } @@@ -3229,21 -3216,91 +3229,21 @@@ else { $value = "'$value'"; } - if ($newName == 'sort_name') { + if ($fieldName == 'sort_name') { $wc = self::caseImportant($op) ? "LOWER(contact_a.sort_name)" : "contact_a.sort_name"; } else { $wc = self::caseImportant($op) ? "LOWER(contact_a.display_name)" : "contact_a.display_name"; } - $sub[] = " ( $wc $op $value )"; + $fieldsub[] = " ( $wc $op $value )"; if ($config->includeNickNameInName) { $wc = self::caseImportant($op) ? "LOWER(contact_a.nick_name)" : "contact_a.nick_name"; - $sub[] = " ( $wc $op $value )"; + $fieldsub[] = " ( $wc $op $value )"; } if ($config->includeEmailInName) { - $sub[] = " ( civicrm_email.email $op $value ) "; - } - } - else { - // the string should be treated as a series of keywords to be matched with match ANY OR - // match ALL depending on Civi config settings (see CiviAdmin) - - // The Civi configuration setting can be overridden if the string *starts* with the case - // insenstive strings 'AND:' or 'OR:'TO THINK ABOUT: what happens when someone searches - // for the following "AND: 'a string in quotes'"? - probably nothing - it would make the - // AND OR variable reduntant because there is only one search string? - - // Check to see if the $subGlue is overridden in the search text - if (strtolower(substr($name, 0, 4)) == 'and:') { - $name = substr($name, 4); - $subGlue = ' AND '; - } - if (strtolower(substr($name, 0, 3)) == 'or:') { - $name = substr($name, 3); - $subGlue = ' OR '; - } - - $firstChar = substr($name, 0, 1); - $lastChar = substr($name, -1, 1); - $quotes = array("'", '"'); - if ((strlen($name) > 2) && in_array($firstChar, $quotes) && - in_array($lastChar, $quotes) - ) { - $name = substr($name, 1); - $name = substr($name, 0, -1); - $pieces = array($name); - } - else { - $pieces = explode(' ', $name); - } - foreach ($pieces as $piece) { - $value = $strtolower(CRM_Core_DAO::escapeString(trim($piece))); - if (strlen($value)) { - // Added If as a sanitization - without it, when you do an OR search, any string with - // double spaces (i.e. " ") or that has a space after the keyword (e.g. "OR: ") will - // return all contacts because it will include a condition similar to "OR contact - // name LIKE '%'". It might be better to replace this with array_filter. - $fieldsub = array(); - if ($wildcard) { - if ($config->includeWildCardInName) { - $value = "'%$value%'"; - } - else { - $value = "'$value%'"; - } - $op = 'LIKE'; - } - else { - $value = "'$value'"; - } - if ($newName == 'sort_name') { - $wc = self::caseImportant($op) ? "LOWER(contact_a.sort_name)" : "contact_a.sort_name"; - } - else { - $wc = self::caseImportant($op) ? "LOWER(contact_a.display_name)" : "contact_a.display_name"; - } - $fieldsub[] = " ( $wc $op $value )"; - if ($config->includeNickNameInName) { - $wc = self::caseImportant($op) ? "LOWER(contact_a.nick_name)" : "contact_a.nick_name"; - $fieldsub[] = " ( $wc $op $value )"; - } - if ($config->includeEmailInName) { - $fieldsub[] = " ( civicrm_email.email $op $value ) "; - } - $sub[] = ' ( ' . implode(' OR ', $fieldsub) . ' ) '; - // I seperated the glueing in two. The first stage should always be OR because we are searching for matches in *ANY* of these fields - } + $fieldsub[] = " ( civicrm_email.email $op $value ) "; } + $sub[] = ' ( ' . implode(' OR ', $fieldsub) . ' ) '; } $sub = ' ( ' . implode($subGlue, $sub) . ' ) '; @@@ -3251,15 -3308,15 +3251,15 @@@ $this->_where[$grouping][] = $sub; if ($config->includeEmailInName) { $this->_tables['civicrm_email'] = $this->_whereTables['civicrm_email'] = 1; - $this->_qill[$grouping][] = ts('Name or Email ') . "$op - '$name'"; + $this->_qill[$grouping][] = ts('Name or Email') . " $op - '$input'"; } else { - $this->_qill[$grouping][] = ts('Name like') . " - '$name'"; + $this->_qill[$grouping][] = ts('Name') . " $op - '$input'"; } } /** - * where / qill clause for email + * Where / qill clause for email * * @param $values * @@@ -3302,7 -3359,7 +3302,7 @@@ } /** - * where / qill clause for phone number + * Where / qill clause for phone number * * @param $values * @@@ -3325,7 -3382,7 +3325,7 @@@ } /** - * where / qill clause for phone type/location + * Where / qill clause for phone type/location * * @param $values * @@@ -3343,7 -3400,7 +3343,7 @@@ } /** - * where / qill clause for street_address + * Where / qill clause for street_address * * @param $values * @@@ -3378,7 -3435,7 +3378,7 @@@ } /** - * where / qill clause for street_unit + * Where / qill clause for street_unit * * @param $values * @@@ -3413,7 -3470,7 +3413,7 @@@ } /** - * where / qill clause for sorting by character + * Where / qill clause for sorting by character * * @param $values * @@@ -3430,7 -3487,7 +3430,7 @@@ } /** - * where / qill clause for including contact ids + * Where / qill clause for including contact ids * * @return void * @access public @@@ -3452,7 -3509,7 +3452,7 @@@ } /** - * where / qill clause for postal code + * Where / qill clause for postal code * * @param $values * @@@ -3494,7 -3551,7 +3494,7 @@@ } /** - * where / qill clause for location type + * Where / qill clause for location type * * @param $values * @param null $status @@@ -3614,7 -3671,7 +3614,7 @@@ } /** - * where / qill clause for county (if present) + * Where / qill clause for county (if present) * * @param $values * @param null $status @@@ -3683,7 -3740,7 +3683,7 @@@ } /** - * where / qill clause for state/province AND country (if present) + * Where / qill clause for state/province AND country (if present) * * @param $values * @param null $status @@@ -3778,7 -3835,7 +3778,7 @@@ } /** - * where / qill clause for change log + * Where / qill clause for change log * * @param $values * @@@ -3798,7 -3855,7 +3798,7 @@@ $name = $targetName[4] ? "%$name%" : $name; $this->_where[$grouping][] = "contact_b_log.sort_name LIKE '%$name%'"; $this->_tables['civicrm_log'] = $this->_whereTables['civicrm_log'] = 1; - $this->_qill[$grouping][] = ts('Modified by') . ": $name"; + $this->_qill[$grouping][] = ts('Modified By') . " $name"; } /** @@@ -3959,7 -4016,7 +3959,7 @@@ } /** - * where / qill clause for relationship + * Where / qill clause for relationship * * @param $values * @@@ -3972,7 -4029,7 +3972,7 @@@ return; } // also get values array for relation_target_name - // for relatinship search we always do wildcard + // for relationship search we always do wildcard $relationType = $this->getWhereValues('relation_type_id', $grouping); $targetName = $this->getWhereValues('relation_target_name', $grouping); $relStatus = $this->getWhereValues('relation_status', $grouping); @@@ -4096,12 -4153,6 +4096,12 @@@ civicrm_relationship.start_date > {$tod $this->_qill[$grouping][] = ts('Relationship - Inactive or not Current'); } + $onlyDeleted = 0; + if (in_array(array('deleted_contacts', '=', '1', '0', '0'), $this->_params)) { + $onlyDeleted = 1; + } + $where[$grouping][] = "(contact_b.is_deleted = {$onlyDeleted})"; + //check for permissioned, non-permissioned and all permissioned relations if ($relPermission[2] == 1) { $where[$grouping][] = "( @@@ -4179,7 -4230,7 +4179,7 @@@ civicrm_relationship.is_permission_a_b } /** - * default set of return properties + * Default set of return properties * * @param int $mode * @@@ -4261,7 -4312,7 +4261,7 @@@ } /** - * get primary condition for a sql clause + * Get primary condition for a sql clause * * @param int $value * @@@ -4277,7 -4328,7 +4277,7 @@@ } /** - * wrapper for a simple search query + * Wrapper for a simple search query * * @param array $params * @param array $returnProperties @@@ -4308,10 -4359,11 +4308,10 @@@ * @param string $sort * @param int $offset * @param int $row_count - * @param bool $smartGroupCache + * @param bool $smartGroupCache ?? update smart group cache? * @param bool $count return count obnly * @param bool $skipPermissions Should permissions be ignored or should the logged in user's permissions be applied * - * @params bool $smartGroupCache ?? update smart group cache? * * @return array * @access public @@@ -4394,7 -4446,7 +4394,7 @@@ } /** - * create and query the db for an contact search + * Create and query the db for an contact search * * @param int $offset the offset for the query * @param int $rowCount the number of rows to return @@@ -4807,7 -4859,7 +4807,7 @@@ SELECT COUNT( conts.total_amount ) as c } /** - * getter for the qill object + * Getter for the qill object * * @return string * @access public @@@ -4817,7 -4869,7 +4817,7 @@@ } /** - * default set of return default hier return properties + * Default set of return default hier return properties * * @return array * @access public @@@ -4902,9 -4954,9 +4902,9 @@@ /** * @param $values - * @param $tableName - * @param $fieldName - * @param $dbFieldName + * @param string $tableName + * @param string $fieldName + * @param string $dbFieldName * @param $fieldTitle * @param bool $appendTimeStamp */ @@@ -5015,9 -5067,9 +5015,9 @@@ /** * @param $values - * @param $tableName - * @param $fieldName - * @param $dbFieldName + * @param string $tableName + * @param string $fieldName + * @param string $dbFieldName * @param $fieldTitle * @param null $options */ @@@ -5329,12 -5381,14 +5329,12 @@@ AND displayRelType.is_active = * @param $op string the sql operator, this function should handle ALL SQL operators * @param $value string|integer|array depends on the operator and who's calling the query builder * @param $grouping int the index where to place the where clause - * @param $selectValues + * @param $selectValues the key value pairs for this element. This allows us to use this function for things besides option-value pairs * @param $field array an array that contains various properties of the field identified by $name * @param $label string The label for this field element * @param $dataType string The data type for this element - * * @param bool $useIDsOnly * - * @internal param array $selectValue the key value pairs for this element. This allows us to use this function for things besides option-value pairs * @return void adds the where clause and qill to the query object */ function optionValueQuery( @@@ -5405,7 -5459,7 +5405,7 @@@ } /** - * function to check and explode a user defined numeric string into an array + * Check and explode a user defined numeric string into an array * this was the protocol used by search builder in the old old days before we had * super nice js widgets to do the hard work * @@@ -5447,7 -5501,7 +5447,7 @@@ } /** - * convert the pseudo constants id's to their names + * Convert the pseudo constants id's to their names * * @param CRM_Core_DAO dao * @param bool $return @@@ -5523,7 -5577,7 +5523,7 @@@ } /** - * include pseudo fields LEFT JOIN + * Include pseudo fields LEFT JOIN * @param string|array $sort can be a object or string * * @return array