diff --git a/CRM/Utils/QueryFormatter.php b/CRM/Utils/QueryFormatter.php index 4f61473dc2f5..cf99ca6cdd8d 100644 --- a/CRM/Utils/QueryFormatter.php +++ b/CRM/Utils/QueryFormatter.php @@ -287,6 +287,17 @@ protected function _formatFts($text, $mode) { */ protected function _formatFtsBool($text, $mode) { $result = NULL; + $operators = array('+', '-', '~', '(', ')'); + + //Return if searched string ends with an unsupported operator. + foreach ($operators as $val) { + if ($text == '@' || CRM_Utils_String::endsWith($text, $val)) { + $csid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', 'CRM_Contact_Form_Search_Custom_FullText', 'value', 'name'); + $url = CRM_Utils_System::url("civicrm/contact/search/custom", "csid={$csid}&reset=1"); + $operators = implode("', '", $operators); + CRM_Core_Error::statusBounce("InnoDB Full-Text Search does not support the use of a string ending with any of these operators ('{$operators}' or a single '@') in boolean mode. These invalid queries return a syntax error.", $url); + } + } // normalize user-inputted wildcards $text = str_replace('%', '*', $text); @@ -309,7 +320,7 @@ protected function _formatFtsBool($text, $mode) { else { switch ($mode) { case self::MODE_NONE: - $result = $this->mapWords($text, '+word'); + $result = $this->mapWords($text, '+word', TRUE); break; case self::MODE_PHRASE: @@ -380,11 +391,13 @@ protected function _formatLike($text, $mode) { * User-supplied query string. * @param string $template * A prototypical description of each word, eg "word%" or "word*" or "*word*". + * @param bool $quotes + * True if each searched keyword need to be surrounded with quotes. * @return string */ - protected function mapWords($text, $template) { + protected function mapWords($text, $template, $quotes = FALSE) { $result = array(); - foreach ($this->parseWords($text) as $word) { + foreach ($this->parseWords($text, $quotes) as $word) { $result[] = str_replace('word', $word, $template); } return implode(' ', $result); @@ -392,9 +405,10 @@ protected function mapWords($text, $template) { /** * @param $text + * @bool $quotes * @return array */ - protected function parseWords($text) { + protected function parseWords($text, $quotes) { //NYSS 9692 special handling for emails if (preg_match('/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/', $text)) { $parts = explode('@', $text); @@ -403,7 +417,20 @@ protected function parseWords($text) { } //NYSS also replace other occurrences of @ - return explode(' ', preg_replace('/[ \r\n\t\@]+/', ' ', trim($text))); + $replacedText = preg_replace('/[ \r\n\t\@]+/', ' ', trim($text)); + + //filter empty values if any + $keywords = array_filter(explode(' ', $replacedText)); + + //Ensure each searched keywords are wrapped in double quotes. + if ($quotes) { + foreach ($keywords as &$val) { + if (!is_numeric($val)) { + $val = "\"{$val}\""; + } + } + } + return $keywords; } /**