From 61fd8e1221887e116d4f916e9be1ef7bb94805b4 Mon Sep 17 00:00:00 2001 From: Benjamin Mack <benni@typo3.org> Date: Sun, 1 Feb 2015 13:09:31 +0100 Subject: [PATCH] [BUGFIX] Indexed search broken after moving "SearchResultContentObject" Indexed search still uses the old cobject in both plugins. The problem arieses in just to call one method from SearchResultContentObject->register_and_explode_search_string() The necessary functions are moved to the common IndexedSearchUtility. Resolves: #64716 Releases: master Change-Id: I386330f1f66342ee838e3616cac7f0924bd31522 Reviewed-on: http://review.typo3.org/36566 Reviewed-by: Tymoteusz Motylewski <t.motylewski@gmail.com> Tested-by: Tymoteusz Motylewski <t.motylewski@gmail.com> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../Classes/Controller/SearchController.php | 9 +- .../Controller/SearchFormController.php | 11 +- .../Classes/Utility/IndexedSearchUtility.php | 115 +++++++++++++++++- 3 files changed, 120 insertions(+), 15 deletions(-) diff --git a/typo3/sysext/indexed_search/Classes/Controller/SearchController.php b/typo3/sysext/indexed_search/Classes/Controller/SearchController.php index c77586ed4ad9..91c3cc5d2baf 100644 --- a/typo3/sysext/indexed_search/Classes/Controller/SearchController.php +++ b/typo3/sysext/indexed_search/Classes/Controller/SearchController.php @@ -729,12 +729,9 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8', $GLOBALS['TSFE']->csConvObj->utf8_encode(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('localizedOperandOr', 'indexed_search'), $GLOBALS['TSFE']->renderCharset), 'toLower'), 'OR'), array($GLOBALS['TSFE']->csConvObj->conv_case('utf-8', $GLOBALS['TSFE']->csConvObj->utf8_encode(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('localizedOperandNot', 'indexed_search'), $GLOBALS['TSFE']->renderCharset), 'toLower'), 'AND NOT') ); - $search = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\SearchResultContentObject::class); - $search->default_operator = $defaultOperator == 1 ? 'OR' : 'AND'; - $search->operator_translate_table = $operatorTranslateTable; - $search->register_and_explode_search_string($searchWords); - if (is_array($search->sword_array)) { - $sWordArray = $this->procSearchWordsByLexer($search->sword_array); + $swordArray = \TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility::getExplodedSearchString($searchWords, $defaultOperator == 1 ? 'OR' : 'AND', $operatorTranslateTable); + if (is_array($swordArray)) { + $sWordArray = $this->procSearchWordsByLexer($swordArray); } } } diff --git a/typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php b/typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php index 45d9f2e7b119..dbf5ccaf3371 100644 --- a/typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php +++ b/typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php @@ -117,7 +117,7 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin { $this->initialize(); // Do search: // If there were any search words entered... - if (is_array($this->sWArr)) { + if (is_array($this->sWArr) && !empty($this->sWArr)) { $content = $this->doSearch($this->sWArr); } // Finally compile all the content, form, messages and results: @@ -354,12 +354,9 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin { // type = Sentence $sWordArray = array(array('sword' => trim($inSW), 'oper' => 'AND')); } else { - $search = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\SearchResultContentObject::class); - $search->default_operator = $defOp == 1 ? 'OR' : 'AND'; - $search->operator_translate_table = $this->operator_translate_table; - $search->register_and_explode_search_string($inSW); - if (is_array($search->sword_array)) { - $sWordArray = $this->procSearchWordsByLexer($search->sword_array); + $searchWords = \TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility::getExplodedSearchString($inSW, $defOp == 1 ? 'OR' : 'AND', $this->operator_translate_table); + if (is_array($searchWords)) { + $sWordArray = $this->procSearchWordsByLexer($searchWords); } } } diff --git a/typo3/sysext/indexed_search/Classes/Utility/IndexedSearchUtility.php b/typo3/sysext/indexed_search/Classes/Utility/IndexedSearchUtility.php index 35b1bedc6bb6..7bd29fdf382a 100644 --- a/typo3/sysext/indexed_search/Classes/Utility/IndexedSearchUtility.php +++ b/typo3/sysext/indexed_search/Classes/Utility/IndexedSearchUtility.php @@ -18,8 +18,6 @@ namespace TYPO3\CMS\IndexedSearch\Utility; * Class with common methods used across various classes in the indexed search. * Impementation is provided by various people from the TYPO3 community. * - * This class is final because it contains only static methods. - * * @author Dmitry Dulepov <dmitry@typo3.com> */ class IndexedSearchUtility { @@ -48,4 +46,117 @@ class IndexedSearchUtility { return hexdec(substr(md5($stringToHash), 0, 7)); } + + /** + * Takes a search-string (WITHOUT SLASHES or else it'll be a little sppooky , NOW REMEMBER to unslash!!) + * Sets up search words with operators. + * + * @param string $sword The input search-word string. + * @param string $defaultOperator + * @param array $operatorTranslateTable + * @return array + */ + public static function getExplodedSearchString($sword, $defaultOperator, $operatorTranslateTable) { + $swordArray = array(); + $sword = trim($sword); + if ($sword) { + $components = self::split($sword); + if (is_array($components)) { + $i = 0; + $lastoper = ''; + foreach ($components as $key => $val) { + $operator = self::getOperator($val, $operatorTranslateTable); + if ($operator) { + $lastoper = $operator; + } elseif (strlen($val) > 1) { + // A searchword MUST be at least two characters long! + $swordArray[$i]['sword'] = $val; + $swordArray[$i]['oper'] = $lastoper ?: $defaultOperator; + $lastoper = ''; + $i++; + } + } + } + } + return $swordArray; + } + + /** + * Used to split a search-word line up into elements to search for. This function will detect boolean words like AND and OR, + and -, and even find sentences encapsulated in "" + * This function could be re-written to be more clean and effective - yet it's not that important. + * + * @param string $origSword The raw sword string from outside + * @param string $specchars Special chars which are used as operators (+- is default) + * @param string $delchars Special chars which are deleted if the append the searchword (+-., is default) + * @return mixed Returns an ARRAY if there were search words, otherwise the return value may be unset. + */ + protected static function split($origSword, $specchars = '+-', $delchars = '+.,-') { + $value = NULL; + $sword = $origSword; + $specs = '[' . preg_quote($specchars, '/') . ']'; + // As long as $sword is TRUE (that means $sword MUST be reduced little by little until its empty inside the loop!) + while ($sword) { + // There was a double-quote and we will then look for the ending quote. + if (preg_match('/^"/', $sword)) { + // Removes first double-quote + $sword = preg_replace('/^"/', '', $sword); + // Removes everything till next double-quote + preg_match('/^[^"]*/', $sword, $reg); + // reg[0] is the value, should not be trimmed + $value[] = $reg[0]; + $sword = preg_replace('/^' . preg_quote($reg[0], '/') . '/', '', $sword); + // Removes last double-quote + $sword = trim(preg_replace('/^"/', '', $sword)); + } elseif (preg_match('/^' . $specs . '/', $sword, $reg)) { + $value[] = $reg[0]; + // Removes = sign + $sword = trim(preg_replace('/^' . $specs . '/', '', $sword)); + } elseif (preg_match('/[\\+\\-]/', $sword)) { + // Check if $sword contains + or - + // + and - shall only be interpreted as $specchars when there's whitespace before it + // otherwise it's included in the searchword (e.g. "know-how") + // explode $sword to single words + $a_sword = explode(' ', $sword); + // get first word + $word = array_shift($a_sword); + // Delete $delchars at end of string + $word = rtrim($word, $delchars); + // add searchword to values + $value[] = $word; + // re-build $sword + $sword = implode(' ', $a_sword); + } else { + // There are no double-quotes around the value. Looking for next (space) or special char. + preg_match('/^[^ ' . preg_quote($specchars, '/') . ']*/', $sword, $reg); + // Delete $delchars at end of string + $word = rtrim(trim($reg[0]), $delchars); + $value[] = $word; + $sword = trim(preg_replace('/^' . preg_quote($reg[0], '/') . '/', '', $sword)); + } + } + return $value; + } + + /** + * This returns an SQL search-operator (eg. AND, OR, NOT) translated from the current localized set of operators (eg. in danish OG, ELLER, IKKE). + * + * @param string $operator The possible operator to find in the internal operator array. + * @param array $operatorTranslateTable an array of possible operators + * @return string If found, the SQL operator for the localized input operator. + * @access private + */ + protected static function getOperator($operator, $operatorTranslateTable) { + $operator = trim($operator); + // case-conversion is charset insensitive, but it doesn't spoil + // anything if input string AND operator table is already converted + $operator = strtolower($operator); + foreach ($operatorTranslateTable as $key => $val) { + $item = $operatorTranslateTable[$key][0]; + // See note above. + $item = strtolower($item); + if ($operator == $item) { + return $operatorTranslateTable[$key][1]; + } + } + } } -- GitLab