diff --git a/Classes/Scheduler/class.tx_kesearch_indexertask.php b/Classes/Scheduler/class.tx_kesearch_indexertask.php index ee42d5f2..c861536c 100644 --- a/Classes/Scheduler/class.tx_kesearch_indexertask.php +++ b/Classes/Scheduler/class.tx_kesearch_indexertask.php @@ -1,46 +1,42 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ - + * Copyright notice + * (c) 2011 Andreas Kiefer + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ // include indexer class require_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search') . 'Classes/indexer/class.tx_kesearch_indexer.php'); -class tx_kesearch_indexertask extends \TYPO3\CMS\Scheduler\Task\AbstractTask { +class tx_kesearch_indexertask extends \TYPO3\CMS\Scheduler\Task\AbstractTask +{ - public function execute() { + public function execute() + { - // get extension configuration - $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); + // get extension configuration + $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); - // make indexer instance - $indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); + // make indexer instance + $indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); - // process - $indexer->startIndexing(true, $this->extConf, 'CLI'); + // process + $indexer->startIndexing(true, $this->extConf, 'CLI'); - return true; - } -} \ No newline at end of file + return true; + } +} diff --git a/Classes/lib/class.tx_kesearch_config.php b/Classes/lib/class.tx_kesearch_config.php index a88f7d65..636deebb 100644 --- a/Classes/lib/class.tx_kesearch_config.php +++ b/Classes/lib/class.tx_kesearch_config.php @@ -1,36 +1,31 @@ pObj = $pObj; - $this->cObj = $this->pObj->cObj; - $this->conf = $this->pObj->conf; - } - - - public function getSearchResults() { - // if there are no searchresults return the empty result array directly - if(!$this->hasSearchResults) return $this->searchResults; - - // if result array is empty start search on DB, else return cached result list - if(!count($this->searchResults)) { - if($this->sphinxSearchEnabled()) { - $this->searchResults = $this->getSearchResultBySphinx(); - } else { - $this->getSearchResultByMySQL(); - } - if($this->getAmountOfSearchResults() === 0) { - $this->hasSearchResults = FALSE; - } - } - return $this->searchResults; - } - - - /** - * get a limitted amount of search results for a requested page - * - * @return array Array containing a limitted (one page) amount of search results - */ - public function getSearchResultByMySQL() { - $queryParts = $this->getQueryParts(); - - // log query - if ($this->conf['logQuery']) { - $query = $GLOBALS['TYPO3_DB']->SELECTquery( - $queryParts['SELECT'], - $queryParts['FROM'], - $queryParts['WHERE'], - $queryParts['GROUPBY'], - $queryParts['ORDERBY'], - $queryParts['LIMIT'] - ); - TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Search result query', $this->pObj->extKey, 0, array($query)); - } - - $this->searchResults = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - $queryParts['SELECT'], - $queryParts['FROM'], - $queryParts['WHERE'], - $queryParts['GROUPBY'], - $queryParts['ORDERBY'], - $queryParts['LIMIT'], - 'uid' - ); - $result = $GLOBALS['TYPO3_DB']->sql_query('SELECT FOUND_ROWS();'); - if($result) { - $data = $GLOBALS['TYPO3_DB']->sql_fetch_row($result); - $GLOBALS['TYPO3_DB']->sql_free_result($result); - $this->numberOfResults = $data[0]; - } - } - - - /** - * Escpapes Query String for Sphinx, taken from SphinxApi.php - * @param string $string - * @return string - */ - function escapeString ( $string ) { - $from = array ( '\\', '(',')','-','!','@','~','"','&', '/', '^', '$', '=' ); - $to = array ( '\\\\', '\(','\)','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' ); - - return str_replace ( $from, $to, $string ); - } - - - /** - * get a limitted amount of search results for a requested page - * - * @return array Array containing a limitted (one page) amount of search results - */ - public function getSearchResultBySphinx() { - require_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search_premium') . 'class.user_kesearchpremium.php'); - $this->user_kesearchpremium = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('user_kesearchpremium'); - - // set ordering - $this->user_kesearchpremium->setSorting($this->getOrdering()); - - // set limit - $limit = $this->getLimit(); - $this->user_kesearchpremium->setLimit($limit[0], $limit[1], intval($this->pObj->extConfPremium['sphinxLimit'])); - - // generate query - $queryForSphinx = ''; - if($this->pObj->wordsAgainst) $queryForSphinx .= ' @(title,content) ' . $this->escapeString($this->pObj->wordsAgainst); - if(count($this->pObj->tagsAgainst)) { - foreach($this->pObj->tagsAgainst as $value) { - // in normal case only checkbox mode has spaces - $queryForSphinx .= ' @tags ' . str_replace('" "', '" | "', trim($value)); - } - } - - // add language - $queryForSphinx .= ' @language _language_-1 | _language_' . $GLOBALS['TSFE']->sys_language_uid; - - // add fe_groups to query - $queryForSphinx .= ' @fe_group _group_NULL | _group_0'; - if(!empty($GLOBALS['TSFE']->gr_list)) { - $feGroups = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TSFE']->gr_list, 1); - foreach($feGroups as $key => $group) { - $intval_positive_group = TYPO3\CMS\Core\Utility\MathUtility::convertToPositiveInteger($group); - if($intval_positive_group) { - $feGroups[$key] = '_group_' . $group; - } else unset($feGroups[$key]); - } - if(is_array($feGroups) && count($feGroups)) $queryForSphinx .= ' | ' . implode(' | ', $feGroups); - } - - // restrict to storage page (in MySQL: $where .= ' AND pid in (' . . ') ';) - $startingPoints = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->pObj->startingPoints); - $queryForSphinx .= ' @pid '; - $first = true; - foreach ($startingPoints as $startingPoint) { - if (!$first) { - $queryForSphinx .= ' | '; - } else { - $first = false; - } - - $queryForSphinx .= ' _pid_' . $startingPoint; - } - - // hook for appending additional where clause to sphinx query - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['appendWhereToSphinx'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['appendWhereToSphinx'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $queryForSphinx = $_procObj->appendWhereToSphinx($queryForSphinx, $this->user_kesearchpremium, $this); - } - } - $rows = $this->user_kesearchpremium->getSearchResults($queryForSphinx); - - // get number of records - $this->numberOfResults = $this->user_kesearchpremium->getTotalFound(); - return $rows; - } - - - /** - * get query parts like SELECT, FROM and WHERE for MySQL-Query - * - * @return array Array containing the query parts for MySQL - */ - public function getQueryParts() { - $fields = 'SQL_CALC_FOUND_ROWS *'; - $table = $this->table . $this->bestIndex; - $where = '1=1'; - - // if a searchword was given, calculate percent of score - if($this->pObj->sword) { - $fields .= ', MATCH (title, content) AGAINST ("' . $this->pObj->scoreAgainst . '") + (' . $this->pObj->extConf['multiplyValueToTitle'] . ' * MATCH (title) AGAINST ("' . $this->pObj->scoreAgainst . '")) AS score'; - // The percentage calculation is really expensive and forces a full table scan for each - // search query. If we don't use the percentage we skip this and can make efficient use - // of the fulltext index. - if($this->conf['showPercentalScore']) { - $fields .= ', IFNULL(ROUND((MATCH (title, content) AGAINST ("' . $this->pObj->scoreAgainst . '") + (' . $this->pObj->extConf['multiplyValueToTitle'] . ' * MATCH (title) AGAINST ("' . $this->pObj->scoreAgainst . '"))) / maxScore * 100), 0) AS percent'; - $table .= ', (SELECT MAX(MATCH (title, content) AGAINST ("' . $this->pObj->scoreAgainst . '") + (' . $this->pObj->extConf['multiplyValueToTitle'] . ' * MATCH (title) AGAINST ("' . $this->pObj->scoreAgainst . '"))) AS maxScore FROM ' . $this->table . ') maxScoreTable'; - } - } - - // add where clause - $where .= $this->getWhere(); - - // add ordering - $orderBy = $this->getOrdering(); - - // add limitation - $limit = $this->getLimit(); - - $queryParts = array( - 'SELECT' => $fields, - 'FROM' => $table, - 'WHERE' => $where, - 'GROUPBY' => '', - 'ORDERBY' => $orderBy, - 'LIMIT' => $limit[0] . ',' . $limit[1] - ); - - // hook for third party applications to manipulate last part of query building - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getQueryParts'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getQueryParts'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $queryParts = $_procObj->getQueryParts($queryParts, $this); - } - } - - return $queryParts; - } - - - /** - * Counts the search results - * It's better to make an additional query than working with SQL_CALC_FOUND_ROWS. Further we don't have to lock tables. - * - * @return integer Amount of SearchResults - */ - public function getAmountOfSearchResults() { - return intval($this->numberOfResults); - } - - - /** - * get all tags which are found in search result - * additional the tags are counted - * - * @return array Array containing the tags as key and the sum as value - */ - public function getTagsFromSearchResult() { - $tags = $tagsForResult = array(); - $tagChar = $this->pObj->extConf['prePostTagChar']; - $tagDivider = $tagChar . ',' . $tagChar; - - if($this->sphinxSearchEnabled()) { - $tagsForResult = $this->getTagsFromSphinx(); - } else { - $tagsForResult = $this->getTagsFromMySQL(); - } - foreach($tagsForResult as $tagSet) { - $tagSet = explode($tagDivider, trim($tagSet, $tagChar)); - foreach($tagSet as $tag) { - $tags[$tag] += 1; - } - } - return $tags; - } - - /** - * Determine the available tags for the search result by looking at - * all the tag fields - * - * @return array - */ - protected function getTagsFromSphinx() { - if(is_array($this->searchResults) && count($this->searchResults)) { - return array_map( - function($row) { return $row['tags']; }, - $this->searchResults - ); - } else { - return array(); - } - } - - /** - * Determine the valid tags by querying MySQL - * - * @return array - */ - protected function getTagsFromMySQL() { - $queryParts = $this->getQueryParts(); - $tagRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - 'tags', - $queryParts['FROM'], - $queryParts['WHERE'], - '', - '', - '', - '' - ); - return array_map( - function($row) { return $row['tags']; }, - $tagRows - ); - } - - /** - * This function is useful to decide which index to use - * In case of the individual amount of results (content and tags) another index can be much faster - * - * Never add ORDER BY, LIMIT or some additional MATCH-parts to this queries, because this slows down the query time. - * - * @param string $searchString - * @param string $tagsString - */ - public function chooseBestIndex($searchString = '', $tags) { - $countQueries = 0; - - // Count results only if it is the first run and a searchword is given - if(!$this->countResultsOfContent && $searchString != '') { - $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( - 'uid', - 'tx_kesearch_index', - 'MATCH (title, content) AGAINST ("' . $searchString . '" IN BOOLEAN MODE)' - ); - $this->countResultsOfContent = $count; - $countQueries++; - } - - // Count results only if it is the first run and a tagstring is given - if(!$this->countResultsOfTags && count($tags)) { - $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( - 'tags', - 'tx_kesearch_index', - '1=1 ' . $this->createQueryForTags($tags) - ); - $this->countResultsOfTags = $count; - $countQueries++; - } - - //decide which index to use - if($countQueries == 2) { - // if there are more results in content than in tags, another index is much faster than the index choosed by MySQL - // With this configuration we speed up our results from 47 sec. to 7 sec. with over 50.000 records - if($this->countContentResult > $this->countTagsResult) { - $this->bestIndex = ' USE INDEX (tag)'; - } else { - $this->bestIndex = ' USE INDEX (titlecontent)'; - } - } else { - // MySQL chooses the best index for you, if only one part (content OR tags) are given - // With this configuration we speed up our results from 7 sec. to 2 sec. with over 50.000 records - $this->bestIndex = ''; - } - } - - - /** - * In checkbox mode we have to create for each checkbox one MATCH-AGAINST-Construct - * So this function returns the complete WHERE-Clause for each tag - * - * @param array $tags - * @return string Query - */ - protected function createQueryForTags(array $tags) { - if(count($tags) && is_array($tags)) { - foreach($tags as $value) { - $value = $GLOBALS['TYPO3_DB']->quoteStr($value, 'tx_kesearch_index'); - $where .= ' AND MATCH (tags) AGAINST (\'' . $value . '\' IN BOOLEAN MODE) '; - } - return $where; - } return ''; - } - - - /** - * get where clause for search results - * - * @return string where clause - */ - public function getWhere() { - // add boolean where clause for searchwords - if($this->pObj->wordsAgainst != '') { - $where .= ' AND MATCH (title, content) AGAINST (\'' . $this->pObj->wordsAgainst . '\' IN BOOLEAN MODE) '; - } - - // add boolean where clause for tags - if(($tagWhere = $this->createQueryForTags($this->pObj->tagsAgainst))) { - $where .= $tagWhere; - } - - // restrict to storage page - $where .= ' AND pid in (' . $this->pObj->startingPoints . ') '; - - // add language - $lang = intval($GLOBALS['TSFE']->sys_language_uid); - $where .= ' AND language IN(' . $lang . ', -1) '; - - // add "tagged content only" searchphrase - if($this->conf['showTaggedContentOnly']) { - $where .= ' AND tags <> ""'; - } - - // add enable fields - $where .= $this->cObj->enableFields($this->table); - - return $where; - } - - - /** - * get ordering for where query - * - * @return string ordering (f.e. score DESC) - */ - public function getOrdering() { - // if the following code fails, fall back to this default ordering - $orderBy = $this->conf['sortWithoutSearchword']; - - // if sorting in FE is allowed - if($this->conf['showSortInFrontend']) { - $piVarsField = $this->pObj->piVars['sortByField']; - $piVarsDir = $this->pObj->piVars['sortByDir']; - $piVarsDir = ($piVarsDir == '') ? 'asc' : $piVarsDir; - if(!empty($piVarsField)) { // if an ordering field is defined by GET/POST - $isInList = TYPO3\CMS\Core\Utility\GeneralUtility::inList($this->conf['sortByVisitor'], $piVarsField); - if($this->conf['sortByVisitor'] != '' && $isInList) { - $orderBy = $piVarsField . ' ' . $piVarsDir; - } // if sortByVisitor is not set OR not in the list of allowed fields then use fallback ordering in "sortWithoutSearchword" - } // if sortByVisitor is not set OR not in the list of allowed fields then use fallback ordering in "sortWithoutSearchword" - } else if (!empty($this->pObj->wordsAgainst)) { // if sorting is predefined by admin - $orderBy = $this->conf['sortByAdmin']; - } else { - $orderBy = $this->conf['sortWithoutSearchword']; - } - - return $orderBy; - } - - - /** - * get limit for where query - */ - public function getLimit() { - $limit = $this->conf['resultsPerPage'] ? $this->conf['resultsPerPage'] : 10; - - if($this->pObj->piVars['page']) { - $start = ($this->pObj->piVars['page'] * $limit) - $limit; - if($start < 0) $start = 0; - } - - $startLimit = array($start, $limit); - - // hook for third party pagebrowsers or for modification $this->pObj->piVars['page'] parameter - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getLimit'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getLimit'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $_procObj->getLimit($startLimit, $this); - } - } - - return $startLimit; - } - - /** - * Check if Sphinx search is enabled - * - * @return boolean - */ - protected function sphinxSearchEnabled() { - return $this->pObj->extConfPremium['enableSphinxSearch'] && !$this->pObj->isEmptySearch; - } -} \ No newline at end of file +class tx_kesearch_db implements \TYPO3\CMS\Core\SingletonInterface +{ + public $conf = array(); + public $bestIndex = ''; + public $countResultsOfTags = 0; + public $countResultsOfContent = 0; + public $table = 'tx_kesearch_index'; + protected $hasSearchResults = true; + protected $searchResults = array(); + protected $numberOfResults = 0; + + /** + * @var tx_kesearch_pi1 + */ + public $pObj; + + /** + * @var tslib_cObj + */ + public $cObj; + + public function __construct($pObj) + { + $this->pObj = $pObj; + $this->cObj = $this->pObj->cObj; + $this->conf = $this->pObj->conf; + } + + /** + * @return array + */ + public function getSearchResults() + { + // if there are no searchresults return the empty result array directly + if (!$this->hasSearchResults) { + return $this->searchResults; + } + + // if result array is empty start search on DB, else return cached result list + if (!count($this->searchResults)) { + if ($this->sphinxSearchEnabled()) { + $this->searchResults = $this->getSearchResultBySphinx(); + } else { + $this->getSearchResultByMySQL(); + } + if ($this->getAmountOfSearchResults() === 0) { + $this->hasSearchResults = false; + } + } + return $this->searchResults; + } + + /** + * get a limitted amount of search results for a requested page + * @return array Array containing a limitted (one page) amount of search results + */ + public function getSearchResultByMySQL() + { + $queryParts = $this->getQueryParts(); + + // log query + if ($this->conf['logQuery']) { + $query = $GLOBALS['TYPO3_DB']->SELECTquery( + $queryParts['SELECT'], + $queryParts['FROM'], + $queryParts['WHERE'], + $queryParts['GROUPBY'], + $queryParts['ORDERBY'], + $queryParts['LIMIT'] + ); + TYPO3\CMS\Core\Utility\GeneralUtility::devLog('Search result query', $this->pObj->extKey, 0, array($query)); + } + + $this->searchResults = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + $queryParts['SELECT'], + $queryParts['FROM'], + $queryParts['WHERE'], + $queryParts['GROUPBY'], + $queryParts['ORDERBY'], + $queryParts['LIMIT'], + 'uid' + ); + $result = $GLOBALS['TYPO3_DB']->sql_query('SELECT FOUND_ROWS();'); + if ($result) { + $data = $GLOBALS['TYPO3_DB']->sql_fetch_row($result); + $GLOBALS['TYPO3_DB']->sql_free_result($result); + $this->numberOfResults = $data[0]; + } + } + + /** + * Escpapes Query String for Sphinx, taken from SphinxApi.php + * + * @param string $string + * @return string + */ + public function escapeString($string) + { + $from = array('\\', '(', ')', '-', '!', '@', '~', '"', '&', '/', '^', '$', '='); + $to = array('\\\\', '\(', '\)', '\-', '\!', '\@', '\~', '\"', '\&', '\/', '\^', '\$', '\='); + + return str_replace($from, $to, $string); + } + + /** + * get a limitted amount of search results for a requested page + * + * @return array Array containing a limitted (one page) amount of search results + */ + public function getSearchResultBySphinx() + { + require_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search_premium') . 'class.user_kesearchpremium.php'); + $this->user_kesearchpremium = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('user_kesearchpremium'); + + // set ordering + $this->user_kesearchpremium->setSorting($this->getOrdering()); + + // set limit + $limit = $this->getLimit(); + $this->user_kesearchpremium->setLimit($limit[0], $limit[1], intval($this->pObj->extConfPremium['sphinxLimit'])); + + // generate query + $queryForSphinx = ''; + if ($this->pObj->wordsAgainst) { + $queryForSphinx .= ' @(title,content) ' . $this->escapeString($this->pObj->wordsAgainst); + } + if (count($this->pObj->tagsAgainst)) { + foreach ($this->pObj->tagsAgainst as $value) { + // in normal case only checkbox mode has spaces + $queryForSphinx .= ' @tags ' . str_replace('" "', '" | "', trim($value)); + } + } + + // add language + $queryForSphinx .= ' @language _language_-1 | _language_' . $GLOBALS['TSFE']->sys_language_uid; + + // add fe_groups to query + $queryForSphinx .= ' @fe_group _group_NULL | _group_0'; + if (!empty($GLOBALS['TSFE']->gr_list)) { + $feGroups = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TSFE']->gr_list, 1); + foreach ($feGroups as $key => $group) { + $intval_positive_group = TYPO3\CMS\Core\Utility\MathUtility::convertToPositiveInteger($group); + if ($intval_positive_group) { + $feGroups[$key] = '_group_' . $group; + } else { + unset($feGroups[$key]); + } + } + if (is_array($feGroups) && count($feGroups)) { + $queryForSphinx .= ' | ' . implode(' | ', $feGroups); + } + } + + // restrict to storage page (in MySQL: $where .= ' AND pid in (' . . ') ';) + $startingPoints = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->pObj->startingPoints); + $queryForSphinx .= ' @pid '; + $first = true; + foreach ($startingPoints as $startingPoint) { + if (!$first) { + $queryForSphinx .= ' | '; + } else { + $first = false; + } + + $queryForSphinx .= ' _pid_' . $startingPoint; + } + + // hook for appending additional where clause to sphinx query + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['appendWhereToSphinx'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['appendWhereToSphinx'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $queryForSphinx = $_procObj->appendWhereToSphinx($queryForSphinx, $this->user_kesearchpremium, $this); + } + } + $rows = $this->user_kesearchpremium->getSearchResults($queryForSphinx); + + // get number of records + $this->numberOfResults = $this->user_kesearchpremium->getTotalFound(); + return $rows; + } + + /** + * get query parts like SELECT, FROM and WHERE for MySQL-Query + * + * @return array Array containing the query parts for MySQL + */ + public function getQueryParts() + { + $fields = 'SQL_CALC_FOUND_ROWS *'; + $table = $this->table . $this->bestIndex; + $where = '1=1'; + + // if a searchword was given, calculate percent of score + if ($this->pObj->sword) { + $fields .= ', MATCH (title, content) AGAINST ("' + . $this->pObj->scoreAgainst + . '") + (' + . $this->pObj->extConf['multiplyValueToTitle'] + . ' * MATCH (title) AGAINST ("' + . $this->pObj->scoreAgainst + . '")) AS score'; + // The percentage calculation is really expensive and forces a full table scan for each + // search query. If we don't use the percentage we skip this and can make efficient use + // of the fulltext index. + if ($this->conf['showPercentalScore']) { + $fields .= ', IFNULL(ROUND((MATCH (title, content) AGAINST ("' + . $this->pObj->scoreAgainst + . '") + (' + . $this->pObj->extConf['multiplyValueToTitle'] + . ' * MATCH (title) AGAINST ("' + . $this->pObj->scoreAgainst + . '"))) / maxScore * 100), 0) AS percent'; + + $table .= ', (SELECT MAX(MATCH (title, content) AGAINST ("' + . $this->pObj->scoreAgainst + . '") + (' + . $this->pObj->extConf['multiplyValueToTitle'] + . ' * MATCH (title) AGAINST ("' + . $this->pObj->scoreAgainst + . '"))) AS maxScore FROM ' + . $this->table + . ') maxScoreTable'; + } + } + + // add where clause + $where .= $this->getWhere(); + + // add ordering + $orderBy = $this->getOrdering(); + + // add limitation + $limit = $this->getLimit(); + + $queryParts = array( + 'SELECT' => $fields, + 'FROM' => $table, + 'WHERE' => $where, + 'GROUPBY' => '', + 'ORDERBY' => $orderBy, + 'LIMIT' => $limit[0] . ',' . $limit[1] + ); + + // hook for third party applications to manipulate last part of query building + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getQueryParts'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getQueryParts'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $queryParts = $_procObj->getQueryParts($queryParts, $this); + } + } + + return $queryParts; + } + + + /** + * Counts the search results + * It's better to make an additional query than working with + * SQL_CALC_FOUND_ROWS. Further we don't have to lock tables. + * + * @return integer Amount of SearchResults + */ + public function getAmountOfSearchResults() + { + return intval($this->numberOfResults); + } + + + /** + * get all tags which are found in search result + * additional the tags are counted + * + * @return array Array containing the tags as key and the sum as value + */ + public function getTagsFromSearchResult() + { + $tags = $tagsForResult = array(); + $tagChar = $this->pObj->extConf['prePostTagChar']; + $tagDivider = $tagChar . ',' . $tagChar; + + if ($this->sphinxSearchEnabled()) { + $tagsForResult = $this->getTagsFromSphinx(); + } else { + $tagsForResult = $this->getTagsFromMySQL(); + } + foreach ($tagsForResult as $tagSet) { + $tagSet = explode($tagDivider, trim($tagSet, $tagChar)); + foreach ($tagSet as $tag) { + $tags[$tag] += 1; + } + } + return $tags; + } + + /** + * Determine the available tags for the search result by looking at + * all the tag fields + * + * @return array + */ + protected function getTagsFromSphinx() + { + if (is_array($this->searchResults) && count($this->searchResults)) { + return array_map( + function ($row) { + return $row['tags']; + }, + $this->searchResults + ); + } else { + return array(); + } + } + + /** + * Determine the valid tags by querying MySQL + * + * @return array + */ + protected function getTagsFromMySQL() + { + $queryParts = $this->getQueryParts(); + $tagRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'tags', + $queryParts['FROM'], + $queryParts['WHERE'], + '', + '', + '', + '' + ); + return array_map( + function ($row) { + return $row['tags']; + }, + $tagRows + ); + } + + /** + * This function is useful to decide which index to use + * In case of the individual amount of results (content and tags) another index can be much faster + * Never add ORDER BY, LIMIT or some additional MATCH-parts to this queries, because this slows down the query time. + * + * @param string $searchString + * @param string $tags + */ + public function chooseBestIndex($searchString = '', $tags = '') + { + $countQueries = 0; + + // Count results only if it is the first run and a searchword is given + if (!$this->countResultsOfContent && $searchString != '') { + $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( + 'uid', + 'tx_kesearch_index', + 'MATCH (title, content) AGAINST ("' . $searchString . '" IN BOOLEAN MODE)' + ); + $this->countResultsOfContent = $count; + $countQueries++; + } + + // Count results only if it is the first run and a tagstring is given + if (!$this->countResultsOfTags && count($tags)) { + $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( + 'tags', + 'tx_kesearch_index', + '1=1 ' . $this->createQueryForTags($tags) + ); + $this->countResultsOfTags = $count; + $countQueries++; + } + + //decide which index to use + if ($countQueries == 2) { + // if there are more results in content than in tags, another index is + // much faster than the index choosed by MySQL + // With this configuration we speed up our results from 47 sec. to 7 sec. with over 50.000 records + if ($this->countContentResult > $this->countTagsResult) { + $this->bestIndex = ' USE INDEX (tag)'; + } else { + $this->bestIndex = ' USE INDEX (titlecontent)'; + } + } else { + // MySQL chooses the best index for you, if only one part (content OR tags) are given + // With this configuration we speed up our results from 7 sec. to 2 sec. with over 50.000 records + $this->bestIndex = ''; + } + } + + /** + * In checkbox mode we have to create for each checkbox one MATCH-AGAINST-Construct + * So this function returns the complete WHERE-Clause for each tag + * + * @param array $tags + * @return string Query + */ + protected function createQueryForTags(array $tags) + { + if (count($tags) && is_array($tags)) { + foreach ($tags as $value) { + $value = $GLOBALS['TYPO3_DB']->quoteStr($value, 'tx_kesearch_index'); + $where .= ' AND MATCH (tags) AGAINST (\'' . $value . '\' IN BOOLEAN MODE) '; + } + return $where; + } + return ''; + } + + /** + * get where clause for search results + * + * @return string where clause + */ + public function getWhere() + { + $where = ''; + + // add boolean where clause for searchwords + if ($this->pObj->wordsAgainst != '') { + $where .= ' AND MATCH (title, content) AGAINST (\'' . $this->pObj->wordsAgainst . '\' IN BOOLEAN MODE) '; + } + + // add boolean where clause for tags + if (($tagWhere = $this->createQueryForTags($this->pObj->tagsAgainst))) { + $where .= $tagWhere; + } + + // restrict to storage page + $where .= ' AND pid in (' . $this->pObj->startingPoints . ') '; + + // add language + $lang = intval($GLOBALS['TSFE']->sys_language_uid); + $where .= ' AND language IN(' . $lang . ', -1) '; + + // add "tagged content only" searchphrase + if ($this->conf['showTaggedContentOnly']) { + $where .= ' AND tags <> ""'; + } + + // add enable fields + $where .= $this->cObj->enableFields($this->table); + + return $where; + } + + + /** + * get ordering for where query + * + * @return string ordering (f.e. score DESC) + */ + public function getOrdering() + { + // if the following code fails, fall back to this default ordering + $orderBy = $this->conf['sortWithoutSearchword']; + + // if sorting in FE is allowed + if ($this->conf['showSortInFrontend']) { + $piVarsField = $this->pObj->piVars['sortByField']; + $piVarsDir = $this->pObj->piVars['sortByDir']; + $piVarsDir = ($piVarsDir == '') ? 'asc' : $piVarsDir; + if (!empty($piVarsField)) { // if an ordering field is defined by GET/POST + $isInList = TYPO3\CMS\Core\Utility\GeneralUtility::inList($this->conf['sortByVisitor'], $piVarsField); + if ($this->conf['sortByVisitor'] != '' && $isInList) { + $orderBy = $piVarsField . ' ' . $piVarsDir; + } + // if sortByVisitor is not set OR not in the list of + // allowed fields then use fallback ordering in "sortWithoutSearchword" + } + // if sortByVisitor is not set OR not in the list of + //allowed fields then use fallback ordering in "sortWithoutSearchword" + } else { + if (!empty($this->pObj->wordsAgainst)) { // if sorting is predefined by admin + $orderBy = $this->conf['sortByAdmin']; + } else { + $orderBy = $this->conf['sortWithoutSearchword']; + } + } + + return $orderBy; + } + + /** + * get limit for where query + * + * @return array + */ + public function getLimit() + { + $limit = $this->conf['resultsPerPage'] ? $this->conf['resultsPerPage'] : 10; + + if ($this->pObj->piVars['page']) { + $start = ($this->pObj->piVars['page'] * $limit) - $limit; + if ($start < 0) { + $start = 0; + } + } + + $startLimit = array($start, $limit); + + // hook for third party pagebrowsers or for modification $this->pObj->piVars['page'] parameter + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getLimit'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['getLimit'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->getLimit($startLimit, $this); + } + } + + return $startLimit; + } + + /** + * Check if Sphinx search is enabled + * + * @return boolean + */ + protected function sphinxSearchEnabled() + { + return $this->pObj->extConfPremium['enableSphinxSearch'] && !$this->pObj->isEmptySearch; + } +} diff --git a/Classes/lib/class.tx_kesearch_filters.php b/Classes/lib/class.tx_kesearch_filters.php index 80542a5c..8eb428fa 100644 --- a/Classes/lib/class.tx_kesearch_filters.php +++ b/Classes/lib/class.tx_kesearch_filters.php @@ -1,279 +1,312 @@ pObj = $pObj; - $this->cObj = $pObj->cObj; - $this->db = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_db'); + /** + * Initializes this object + * @param tx_kesearch_lib $pObj + * @return void + */ + public function initialize(tx_kesearch_lib $pObj) + { + $this->pObj = $pObj; + $this->cObj = $pObj->cObj; + $this->db = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_db'); - $this->conf = $this->pObj->conf; - $this->piVars = $this->pObj->piVars; - $this->startingPoints = $this->pObj->startingPoints; - $this->tagChar = $this->pObj->extConf['prePostTagChar']; + $this->conf = $this->pObj->conf; + $this->piVars = $this->pObj->piVars; + $this->startingPoints = $this->pObj->startingPoints; + $this->tagChar = $this->pObj->extConf['prePostTagChar']; - // get filters and filter options - $this->filters = $this->getFiltersFromUidList($this->combineLists($this->conf['filters'], $this->conf['hiddenfilters'])); + // get filters and filter options + $this->filters = $this->getFiltersFromUidList( + $this->combineLists($this->conf['filters'], $this->conf['hiddenfilters']) + ); - // hook to modify filters - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilters'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilters'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $_procObj->modifyFilters($this->filters, $this); - } - } + // hook to modify filters + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilters'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilters'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->modifyFilters($this->filters, $this); + } + } - // get list of selected filter options (via frontend or backend) - foreach ($this->filters as $filter) { - $this->filters[$filter['uid']]['selectedOptions'] = $this->getSelectedFilterOptions($filter); - } + // get list of selected filter options (via frontend or backend) + foreach ($this->filters as $filter) { + $this->filters[$filter['uid']]['selectedOptions'] = $this->getSelectedFilterOptions($filter); + } + } - } + /** + * Finds the selected filter options for a given filter. + * Checks + * - piVars one-dimensional filter + * - piVars multi-dimensional filter + * - backend preselected filter options + * returns the filter options uids as values of an array or zero if no option has been selected. + * + * @param array $filter + * @return array + * @author Christian Bülter + * @since 09.09.14 + */ + public function getSelectedFilterOptions($filter) + { + $selectedOptions = array(); - /** - * Finds the selected filter options for a given filter. - * Checks - * - piVars one-dimensional filter - * - piVars multi-dimensional filter - * - backend preselected filter options - * - * returns the filter options uids as values of an array or zero if no option has been selected. - * - * @param array $filter - * @return integer - * @author Christian Bülter - * @since 09.09.14 - */ - public function getSelectedFilterOptions($filter) { - $selectedOptions = array(); + // run through all the filter options and check if one of them + // has been selected. + foreach ($filter['options'] as $option) { + // Is the filter option selected in the frontend via piVars + // or in the backend via flexform configuration ("preselected filters")? + $selected = false; - // run through all the filter options and check if one of them - // has been selected. - foreach ($filter['options'] as $option) { - // Is the filter option selected in the frontend via piVars - // or in the backend via flexform configuration ("preselected filters")? - $selected = false; + if ($this->pObj->piVars['filter'][$filter['uid']] == $option['tag']) { + $selected = true; + } else { + if (is_array($this->pObj->piVars['filter'][$filter['uid']])) { + $isInArray = TYPO3\CMS\Core\Utility\GeneralUtility::inArray( + $this->pObj->piVars['filter'][$filter['uid']], + $option['tag'] + ); + if ($isInArray) { + $selected = true; + } + } else { + if (!isset($this->pObj->piVars['filter'][$filter['uid']]) + && !is_array($this->pObj->piVars['filter'][$filter['uid']]) + ) { + if (is_array($this->pObj->preselectedFilter) + && $this->pObj->in_multiarray($option['tag'], $this->pObj->preselectedFilter) + ) { + $selected = true; + // add preselected filter to piVars + $this->pObj->piVars['filter'][$filter['uid']] = array($option['uid'] => $option['tag']); + } + } + } + } - if ($this->pObj->piVars['filter'][$filter['uid']] == $option['tag']) { - $selected = true; - } else if (is_array($this->pObj->piVars['filter'][$filter['uid']])) { - $isInArray = TYPO3\CMS\Core\Utility\GeneralUtility::inArray($this->pObj->piVars['filter'][$filter['uid']], $option['tag']); - if($isInArray) { - $selected = true; - } - } else if (!isset($this->pObj->piVars['filter'][$filter['uid']]) && !is_array($this->pObj->piVars['filter'][$filter['uid']])) { - if (is_array($this->pObj->preselectedFilter) && $this->pObj->in_multiarray($option['tag'], $this->pObj->preselectedFilter)) { - $selected = true; - // add preselected filter to piVars - $this->pObj->piVars['filter'][$filter['uid']] = array($option['uid'] => $option['tag']); - } - } + if ($selected) { + $selectedOptions[] = $option['uid']; + } + } - if ($selected) { - $selectedOptions[] = $option['uid']; - } - } + return $selectedOptions; + } - return $selectedOptions; - } + /** + * combines two string comma lists + * @param string $list1 + * @param string $list2 + * @author Christian Bülter + * @since 23.07.13 + * @return string + */ + public function combineLists($list1 = '', $list2 = '') + { + if (!empty($list2) && !empty($list2)) { + $list1 .= ','; + } + $list1 .= $list2; + $returnValue = TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList($list1); + return $returnValue; + } - /** - * - * combines two string comma lists - * - * @param string $list1 - * @param string $list2 - * @author Christian Bülter - * @since 23.07.13 - * @return string - */ - public function combineLists($list1 = '', $list2 = '') { - if (!empty($list2) && !empty($list2)) { - $list1 .= ','; - } - $list1 .= $list2; - $returnValue = TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList($list1); - return $returnValue; - } + /** + * get filters and options as associative array + * + * @return array Filters with including Options + */ + public function getFilters() + { + return $this->filters; + } - /** - * get filters and options as associative array - * - * @return array Filters with including Options - */ - public function getFilters() { - return $this->filters; - } + /** + * get the filter records from DB which are configured in FlexForm + * + * @param string $filterUids A commaseperated list of filter uids + * @return array Array with filter records + */ + public function getFiltersFromUidList($filterUids) + { + if (empty($filterUids)) { + return array(); + } + $fields = '*'; + $table = 'tx_kesearch_filters'; + $where = 'pid in (' . $GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table) . ')'; + $where .= ' AND find_in_set(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($filterUids, 'tx_kesearch_filters') . '")'; + $where .= $this->cObj->enableFields($table); + $rows = $this->languageOverlay( + $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + $fields, + $table, + $where, + '', + 'find_in_set(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($filterUids, 'tx_kesearch_filters') . '")', + '', + 'uid' + ), + $table + ); + return $this->addOptionsToFilters($rows); + } - /** - * get the filter records from DB which are configured in FlexForm - * - * @param string $filterUids A commaseperated list of filter uids - * @return array Array with filter records - */ - public function getFiltersFromUidList($filterUids) { - if(empty($filterUids)) return array(); - $fields = '*'; - $table = 'tx_kesearch_filters'; - $where = 'pid in (' . $GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table) . ')'; - $where .= ' AND find_in_set(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($filterUids, 'tx_kesearch_filters') . '")'; - $where .= $this->cObj->enableFields($table); - $rows = $this->languageOverlay( - $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($fields, $table, $where, '', 'find_in_set(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($filterUids, 'tx_kesearch_filters') . '")', '', 'uid'), - $table - ); - return $this->addOptionsToFilters($rows); - } + /** + * get the option records from DB which are configured as commaseperate list within the filter records + * @param string $optionUids A commaseperated list of option uids + * @return array Array with option records + */ + public function getOptionsFromUidList($optionUids) + { + if (empty($optionUids)) { + return array(); + } + $fields = '*'; + $table = 'tx_kesearch_filteroptions'; + $where = 'FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionUids, $table) . '")'; + $where .= ' AND pid in (' . $GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table) . ')'; + $where .= $this->cObj->enableFields($table); + return $this->languageOverlay( + $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + $fields, + $table, + $where, + '', + 'FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionUids, $table) . '")', + '', + 'uid' + ), + $table + ); + } - /** - * get the option records from DB which are configured as commaseperate list within the filter records - * - * @param string $optionUids A commaseperated list of option uids - * @return array Array with option records - */ - public function getOptionsFromUidList($optionUids) { - if(empty($optionUids)) return array(); - $fields = '*'; - $table = 'tx_kesearch_filteroptions'; - $where = 'FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionUids, $table) . '")'; - $where .= ' AND pid in (' . $GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table) . ')'; - $where .= $this->cObj->enableFields($table); - return $this->languageOverlay( - $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - $fields, $table, $where, - '', 'FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionUids, $table) . '")', '', 'uid' - ), - $table - ); - } + /** + * replace the commaseperated option list with the original option records from DB + * @param array $rows The filter records as array + * @return array The filter records where the option value was replaced with the option records as array + */ + public function addOptionsToFilters(array $rows) + { + if (is_array($rows) && count($rows)) { + foreach ($rows as $key => $row) { + if (!empty($row['options'])) { + $rows[$key]['options'] = $this->getOptionsFromUidList($row['options']); + } else { + $rows[$key]['options'] = array(); + } + } + return $rows; + } else { + return array(); + } + } - /** - * replace the commaseperated option list with the original option records from DB - * - * @param array $rows The filter records as array - * @return array The filter records where the option value was replaced with the option records as array - */ - public function addOptionsToFilters(array $rows) { - if(is_array($rows) && count($rows)) { - foreach($rows as $key => $row) { - if(!empty($row['options'])) { - $rows[$key]['options'] = $this->getOptionsFromUidList($row['options']); - } else $rows[$key]['options'] = array(); - } - return $rows; - } else return array(); - } + /** + * Translate the given records + * @param array $rows The records which have to be translated + * @param string $table Define the table from where the records come from + * @return array The localized records + */ + public function languageOverlay(array $rows, $table) + { + if (is_array($rows) && count($rows)) { + foreach ($rows as $key => $row) { + if (is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { + $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( + $table, + $row, + $GLOBALS['TSFE']->sys_language_content, + $GLOBALS['TSFE']->sys_language_contentOL + ); + $rows[$key] = $row; + } + } + return $rows; + } else { + return array(); + } + } - /** - * Translate the given records - * - * @param array $rows The records which have to be translated - * @param string $table Define the table from where the records come from - * @return array The localized records - */ - public function languageOverlay(array $rows, $table) { - if(is_array($rows) && count($rows)) { - foreach($rows as $key => $row) { - if(is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { - $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( - $table, $row, - $GLOBALS['TSFE']->sys_language_content, - $GLOBALS['TSFE']->sys_language_contentOL - ); - $rows[$key] = $row; - } - } - return $rows; - } else return array(); - } + /** + * check if an allowed tag (defined in a filteroption) was found in the current result list + * @param string $tag The tag to match against the searchresult + * @return boolean TRUE if tag was found. Else FALSE + */ + public function checkIfTagMatchesRecords($tag) + { - /** - * check if an allowed tag (defined in a filteroption) was found in the current result list - * - * @param string $tag The tag to match against the searchresult - * @return boolean TRUE if tag was found. Else FALSE - */ - public function checkIfTagMatchesRecords($tag) { + // if tag list is empty, fetch them from the result list + // otherwise use the cached result list + if (!$this->tagsInSearchResult) { + $this->tagsInSearchResult = $this->pObj->tagsInSearchResult = $this->db->getTagsFromSearchResult(); + $GLOBALS['TSFE']->fe_user->setKey('ses', 'ke_search.tagsInSearchResults', $tagsInSearchResult); + } - // if tag list is empty, fetch them from the result list - // otherwise use the cached result list - if (!$this->tagsInSearchResult) { - $this->tagsInSearchResult = $this->pObj->tagsInSearchResult = $this->db->getTagsFromSearchResult(); - $GLOBALS['TSFE']->fe_user->setKey('ses', 'ke_search.tagsInSearchResults', $tagsInSearchResult); - } + return array_key_exists($tag, $this->tagsInSearchResult); + } - return array_key_exists($tag, $this->tagsInSearchResult); - } - - /** - * returns the tag char: a character which wraps tags in the database - * - * @return string - */ - public function getTagChar() { - return $this->tagChar; - } + /** + * returns the tag char: a character which wraps tags in the database + * @return string + */ + public function getTagChar() + { + return $this->tagChar; + } } + diff --git a/Classes/lib/class.tx_kesearch_lib.php b/Classes/lib/class.tx_kesearch_lib.php index 43e77457..e97dcfea 100644 --- a/Classes/lib/class.tx_kesearch_lib.php +++ b/Classes/lib/class.tx_kesearch_lib.php @@ -1,1439 +1,1559 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ + * Copyright notice + * (c) 2010 Andreas Kiefer (kennziffer.com) + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ use \TYPO3\CMS\Core\Utility\GeneralUtility; use \TYPO3\CMS\Core\Utility\ExtensionManagementUtility; /** * Parent class for plugins pi1 and pi2 - * - * @author Andreas Kiefer - * @author Stefan Froemken - * @author Christian Bülter - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @author Stefan Froemken + * @author Christian Bülter + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_lib extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin { - var $prefixId = 'tx_kesearch_pi1'; // Same as class name - var $extKey = 'ke_search'; // The extension key. - - var $sword = ''; // cleaned searchword (karl-heinz => karl heinz) - var $swords = ''; // searchwords as array - var $wordsAgainst = ''; // searchphrase for boolean mode (+karl* +heinz*) - var $tagsAgainst = ''; // tagsphrase for boolean mode (+#category_213# +#city_42#) - var $scoreAgainst = ''; // searchphrase for score/non boolean mode (karl heinz) - var $isEmptySearch = true; // true if no searchparams given; otherwise false - - var $startingPoints = 0; // comma seperated list of startingPoints - var $firstStartingPoint = 0; // first entry in list of startingpoints - - var $conf = array(); // FlexForm-Configuration - var $extConf = array(); // Extension-Configuration - var $extConfPremium = array(); // Extension-Configuration of ke_search_premium if installed - - var $numberOfResults = 0; // count search results - var $indexToUse = ''; // it's for 'USE INDEX ($indexToUse)' to speed up queries - var $tagsInSearchResult = false; // contains all tags of current search result - var $preselectedFilter = array(); // preselected filters by flexform - var $filtersFromFlexform = array(); // array with filter-uids as key and whole data as value - var $hasTooShortWords = false; // contains a boolean value which represents if there are too short words in the searchstring - var $fileTypesWithPreviewPossible = array('pdf', 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'tiff'); - var $fluidTemplateVariables = array(); - - /** - * @var tx_xajax - */ - var $xajax; - - /** - * @var tx_kesearch_db - */ - var $db; - - /** - * @var tx_kesearch_lib_div - */ - var $div; - - /** - * @var user_kesearchpremium - */ - var $user_kesearchpremium; - - /** - * @var tx_kesearch_lib_searchresult - */ - var $searchResult; - - /** - * @var tx_kesearch_filters - */ - var $filters; - - /** - * Initializes flexform, conf vars and some more - * - * @return void - */ - public function init() { - // get some helper functions - $this->div = GeneralUtility::makeInstance('tx_kesearch_lib_div', $this); - - // set start of query timer - if(!$GLOBALS['TSFE']->register['ke_search_queryStartTime']) $GLOBALS['TSFE']->register['ke_search_queryStartTime'] = GeneralUtility::milliseconds(); - - // make settings from flexform available in general configuration ($this->conf) - $this->moveFlexFormDataToConf(); - - // in pi2 (the list plugin) fetch the configuration from pi1 (the search - // box plugin) since all the configuration is done there - if(!empty($this->conf['loadFlexformsFromOtherCE'])) { - $data = $this->pi_getRecord('tt_content', intval($this->conf['loadFlexformsFromOtherCE'])); - $this->cObj->data = $data; - $this->moveFlexFormDataToConf(); - } - - // clean piVars - $this->piVars = $this->div->cleanPiVars($this->piVars); - - // get preselected filter from rootline - $this->getFilterPreselect(); - - // add stdWrap properties to each config value (not to arrays) - foreach($this->conf as $key => $value) { - if (!is_array($this->conf[$key])) { - $this->conf[$key] = $this->cObj->stdWrap($value, $this->conf[$key . '.']); - } - } - - // set some default values (this part have to be after stdWrap!!!) - if(!$this->conf['resultPage']) $this->conf['resultPage'] = $GLOBALS['TSFE']->id; - if(!isset($this->piVars['page'])) $this->piVars['page'] = 1; - if(!empty($this->conf['additionalPathForTypeIcons'])) { - $this->conf['additionalPathForTypeIcons'] = rtrim($this->conf['additionalPathForTypeIcons'], '/') . '/'; - } - - // hook: modifyFlexFormData - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFlexFormData'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFlexFormData'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->modifyFlexFormData($this->conf, $this->cObj, $this->piVars); - } - } - - // prepare database object - $this->db = GeneralUtility::makeInstance('tx_kesearch_db', $this); - - // set startingPoints - $this->startingPoints = $this->div->getStartingPoint(); - - // get filter class - $this->filters = GeneralUtility::makeInstance('tx_kesearch_filters'); - - // get extension configuration array - $this->extConf = tx_kesearch_helper::getExtConf(); - $this->extConfPremium = tx_kesearch_helper::getExtConfPremium(); - - // initialize filters - $this->filters->initialize($this); - - // init templating (marker based or fluid) - $this->initTemplate(); - - // get first startingpoint - $this->firstStartingPoint = $this->div->getFirstStartingPoint($this->startingPoints); - - // build words searchphrase - $searchPhrase = GeneralUtility::makeInstance('tx_kesearch_lib_searchphrase'); - $searchPhrase->initialize($this); - $searchWordInformation = $searchPhrase->buildSearchPhrase(); - - // Hook: modifySearchWords - if(isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifySearchWords'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifySearchWords'] as $classRef) { - $hookObj = GeneralUtility::getUserObj($classRef); - if(method_exists($hookObj, 'modifySearchWords')) { - $hookObj->modifySearchWords($searchWordInformation, $this); - } - } - } - - // set searchword and tag information - $this->sword = $searchWordInformation['sword']; - $this->swords = $searchWordInformation['swords']; - $this->wordsAgainst = $searchWordInformation['wordsAgainst']; - $this->tagsAgainst = $searchWordInformation['tagsAgainst']; - $this->scoreAgainst = $searchWordInformation['scoreAgainst']; - - $this->isEmptySearch = $this->isEmptySearch(); - - // Since sorting for "relevance" in most cases ist the most useful option and - // this sorting option is not available until a searchword is given, make it - // the default sorting after a searchword has been given. - // Set default sorting to "relevance" if the following conditions are true: - // * sorting by user is allowed - // * sorting for "relevance" is allowed (internal: "score") - // * user did not select his own sorting yet - // * a searchword is given - $isInList = GeneralUtility::inList($this->conf['sortByVisitor'], 'score'); - if ($this->conf['showSortInFrontend'] && $isInList && !$this->piVars['sortByField'] && $this->sword) { - $this->piVars['sortByField'] = 'score'; - $this->piVars['sortByDir'] = 'desc'; - } - - // after the searchword is removed, sorting for "score" is not possible - // anymore. So remove this sorting here and put it back to default. - if (!$this->sword && $this->piVars['sortByField'] == 'score') { - unset($this->piVars['sortByField']); - unset($this->piVars['sortByDir']); - } - - // chooseBestIndex is only needed for MySQL-Search. Not for Sphinx - if (!$this->extConfPremium['enableSphinxSearch']) { - // precount results to find the best index - $this->db->chooseBestIndex($this->wordsAgainst, $this->tagsAgainst); - } - - // perform search at this point already if we need to calculate what - // filters to display. - if ($this->conf['checkFilterCondition'] != 'none') { - $this->db->getSearchResults(); - } - - // add cssTag to header if set - $cssFile = $GLOBALS['TSFE']->tmpl->getFileName($this->conf['cssFile']); - if(!empty($cssFile)) { - $GLOBALS['TSFE']->getPageRenderer()->addCssFile($cssFile); - } - } - - /** - * - * initializes the fluid template - */ - public function initTemplate() { - // set default template paths - $this->conf['templateRootPath'] = - GeneralUtility::getFileAbsFileName($this->conf['templateRootPath'] - ? $this->conf['templateRootPath'] - : 'EXT:ke_search/Resources/Private/Templates/'); - - $this->conf['partialRootPath'] = - GeneralUtility::getFileAbsFileName($this->conf['partialRootPath'] - ? $this->conf['partialRootPath'] - : 'EXT:ke_search/Resources/Private/Partials/'); - - $this->conf['layoutRootPath'] - = GeneralUtility::getFileAbsFileName($this->conf['layoutRootPath'] - ? $this->conf['layoutRootPath'] - : 'EXT:ke_search/Resources/Private/Layouts/'); - } - - /** - * Move all FlexForm data of current record to conf array - */ - public function moveFlexFormDataToConf() { - // don't move this to init - $this->pi_initPIflexForm(); - - $piFlexForm = $this->cObj->data['pi_flexform']; - if(is_array($piFlexForm['data'])) { - foreach($piFlexForm['data'] as $sheetKey => $sheet) { - foreach($sheet as $lang) { - foreach($lang as $key => $value) { - // delete current conf value from conf-array when FF-Value differs from TS-Conf and FF-Value is not empty - $value = $this->fetchConfigurationValue($key, $sheetKey); - if($this->conf[$key] != $value && !empty($value)) { - unset($this->conf[$key]); - $this->conf[$key] = $this->fetchConfigurationValue($key, $sheetKey); - } - } - } - } - } - } - - /** - * creates the searchbox - * fills fluid variables for the fluid template to $this->fluidTemplateVariables - * - * @return void - */ - public function getSearchboxContent() { - - // set page = 1 for every new search - $pageValue = 1; - $this->fluidTemplateVariables['page'] = $pageValue; - - // submit - $this->fluidTemplateVariables['submitAltText'] = $this->pi_getLL('submit'); - - // searchword input value - $searchString = $this->piVars['sword']; - - if(!empty($searchString) && $searchString != $this->pi_getLL('searchbox_default_value')) { - $this->swordValue = $searchString; - } else { - $this->swordValue = ''; - } - - $this->fluidTemplateVariables['searchword'] = htmlspecialchars($this->swordValue); - $this->fluidTemplateVariables['searchwordDefault'] = htmlspecialchars($this->pi_getLL('searchbox_default_value')); - $this->fluidTemplateVariables['sortByField'] = $this->piVars['sortByField']; - $this->fluidTemplateVariables['sortByDir'] = $this->piVars['sortByDir']; - - // get filters - $renderedFilters = $this->renderFilters(); - $this->fluidTemplateVariables['filter'] = $renderedFilters; - - // set form action pid - $this->fluidTemplateVariables['targetpage'] = $this->conf['resultPage']; - - // set form action - $siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); - $lParam = GeneralUtility::_GET('L'); - $mpParam = GeneralUtility::_GET('MP'); - $typeParam = GeneralUtility::_GP('type'); - $actionUrl = $siteUrl . 'index.php'; - $this->fluidTemplateVariables['actionUrl'] = $actionUrl; - - // language parameter - if (isset($lParam)) { - $hiddenFieldValue = intval($lParam); - $this->fluidTemplateVariables['lparam'] = $hiddenFieldValue; - } - - // mountpoint parameter - if (isset($mpParam)) { - // the only allowed characters in the MP parameter are digits and , and - - $hiddenFieldValue = preg_replace('/[^0-9,-]/', '', $mpParam); - $this->fluidTemplateVariables['mpparam'] = $hiddenFieldValue; - } - - // type param - if ($typeParam) { - $hiddenFieldValue = intval($typeParam); - $this->fluidTemplateVariables['typeparam'] = $hiddenFieldValue; - } - - // set reset link - unset($linkconf); - $linkconf['parameter'] = $this->conf['resultPage']; - $resetUrl = $this->cObj->typoLink_URL($linkconf); - $this->fluidTemplateVariables['resetUrl'] = $resetUrl; - } - - /** - * loop through all available filters and compile the values for the fluid template rendering - * - */ - public function renderFilters() { - foreach ($this->filters->getFilters() as $filter) { - - // if the current filter is a "hidden filter", skip - // rendering of this filter. The filter is only used - // to add preselected filter options to the query and - // must not be rendered. - $isInList = GeneralUtility::inList($this->conf['hiddenfilters'], $filter['uid']); - - if ($isInList) { - continue; - } - - // get filter options which should be displayed - $options = $this->findFilterOptionsToDisplay($filter); - - // alphabetical sorting of filter options - if ($filter['alphabeticalsorting'] == 1) { - $this->sortArrayByColumn($options, 'title'); - } - - // hook for modifying filter options - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptionsArray'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptionsArray'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $options = $_procObj->modifyFilterOptionsArray($filter['uid'], $options, $this); - } - } - - // build link to reset this filter while keeping the others - unset($linkconf); - $linkconf['parameter'] = $GLOBALS['TSFE']->id; - $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]='.$this->piVars['sword']; - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filter['uid'] . ']='; - if (is_array($this->piVars['filter']) && count($this->piVars['filter'])) { - foreach ($this->piVars['filter'] as $key => $value) { - if ($key != $filter['uid']) { - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $key . ']=' . $value; - } - } - } - $resetLink = $this->cObj->typoLink_URL($linkconf); - - // set values for fluid template - $filterData = $filter; - $filterData['name'] = 'tx_kesearch_pi1[filter][' . $filter['uid'] . ']'; - $filterData['id'] = 'filter_' . $filter['uid']; - $filterData['options'] = $options; - $filterData['checkboxOptions'] = $this->compileCheckboxOptions($filter, $options); - $filterData['optionCount'] = count($options); - $filterData['resetLink'] = $resetLink; - - // special classes / custom code - switch($filter['rendertype']) { - case 'textlinks': - $textLinkObj = GeneralUtility::makeInstance('tx_kesearch_lib_filters_textlinks', $this); - $textLinkObj->renderTextlinks($filter['uid'], $options, $this); - break; - - // use custom code for filter rendering - // set $filterData['rendertype'] = 'custom' - // and $filterData['rawHtmlContent'] to your pre-rendered filter code - default: - // hook for custom filter renderer - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['customFilterRenderer'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['customFilterRenderer'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->customFilterRenderer($filter['uid'], $options, $this, $filterData); - } - } - break; - } - - // add values to fluid template - $this->fluidTemplateVariables['filters'][] = $filterData; - } - } - - /** - * compiles a list of checkbox records - * - * @param integer $filterUid UID of the filter for which we need the checkboxes - * @param array $options contains all options which are found in the search result - * @return array list of checkboxes records - */ - public function compileCheckboxOptions($filter, $options) { - $allOptionsOfCurrentFilter = $filter['options']; - - // alphabetical sorting of filter options - if ($filter['alphabeticalsorting'] == 1) { - $this->sortArrayByColumn($allOptionsOfCurrentFilter, 'title'); - } - - // loop through options - $checkboxOptions = array(); - if(is_array($allOptionsOfCurrentFilter)) { - foreach($allOptionsOfCurrentFilter as $key => $data) { - $data['key'] = $key; - - // check if current option (of searchresults) is in array of all possible options - $isOptionInOptionArray = false; - if (is_array($options)) { - foreach($options as $optionInResultList) { - if ($optionInResultList['value'] == $data['tag']) { - $isOptionInOptionArray = true; - $data['results'] = $optionInResultList['results']; - break; - } - } - } - - // if option is in optionArray, we have to mark the checkboxes - if ($isOptionInOptionArray) { - // if user has selected a checkbox it must be selected on the resultpage, too. - // options which have been preselected in the backend are already in $this->piVars['filter'][$filter['uid]] - if($this->piVars['filter'][$filter['uid']][$key]) { - $data['selected'] = 1; - } - - // mark all checkboxes if that config options is set and no search string was given and there - // are no preselected filters given for that filter - if($this->isEmptySearch && $filter['markAllCheckboxes'] && empty($this->preselectedFilter[$filter['uid']])) { - $data['selected'] = 1; - } - - } else { // if an option was not found in the search results - $data['disabled'] = 1; - } - - $data['id'] = 'filter_' . $filter['uid'] . '_' . $key; - $checkboxOptions[] = $data; - } - } - - // modify filter options by hook - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->modifyFilterOptions($filter, $checkboxOptions, $this); - } - } - - return $checkboxOptions; - } - - /** - * find out which filter options should be displayed for the given filter - * check filter options availability and preselection status - * - * @param array $filter - * @return array - * @author Christian Bülter - * @since 09.09.14 - */ - public function findFilterOptionsToDisplay($filter) { - $options = array(); - - foreach ($filter['options'] as $option) { - - // should we check if the filter option is available in - // the current search result? - if ($this->conf['checkFilterCondition'] != 'none') { - - // Once one filter option has been selected, don't display the - // others anymore since this leads to a strange behaviour (options are - // only displayed if they have BOTH tags: the selected and the other filter option. - if ((!count($filter['selectedOptions']) || in_array($option['uid'], $filter['selectedOptions'])) && $this->filters->checkIfTagMatchesRecords($option['tag'])) { - - // build link which selects this option and keeps all the other selected filters - unset($linkconf); - $linkconf['parameter'] = $GLOBALS['TSFE']->id; - $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' . $this->piVars['sword'] . '&tx_kesearch_pi1[filter][' . $filter['uid'] . ']=' . $option['tag']; - $linkconf['useCacheHash'] = false; - if (is_array($this->piVars['filter']) && count($this->piVars['filter'])) { - foreach ($this->piVars['filter'] as $key => $value) { - if ($key != $filter['uid']) { - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $key . ']=' . $value; - } - } - } - $optionLink = $this->cObj->typoLink_URL($linkconf); - - $options[$option['uid']] = array( - 'title' => $option['title'], - 'value' => $option['tag'], - 'results' => $this->tagsInSearchResult[$option['tag']], - 'selected' => is_array($filter['selectedOptions']) && in_array($option['uid'], $filter['selectedOptions']), - 'link' => $optionLink - ); - } - } else { - // do not process any checks; show all filter options - $options[$option['uid']] = array( - 'title' => $option['title'], - 'value' => $option['tag'], - 'selected' => - is_array($filter['selectedOptions']) - && !empty($filter['selectedOptions']) - && in_array($option['uid'], $filter['selectedOptions']), - ); - } - } - - return $options; - } - - /** - * renders brackets around the number of results, returns an empty - * string if there are no results or if an option for this filter already - * has been selected. - * - * @param integer $numberOfResults - * @param array $filter - * @return string - */ - public function renderNumberOfResultsString($numberOfResults, $filter) { - if ($filter['shownumberofresults'] && !count($filter['selectedOptions']) && $numberOfResults) { - $returnValue = ' (' . $numberOfResults . ')'; - } else { - $returnValue = ''; - } - return $returnValue; - } - - /** - * get all filters configured in FlexForm - * - * @return array Array with filter UIDs - */ - public function getFiltersFromFlexform() { - if(!empty($this->conf['filters']) && count($this->filtersFromFlexform) == 0) { - $fields = '*'; - $table = 'tx_kesearch_filters'; - $where = 'pid in ('.$GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table).')'; - $where .= ' AND uid in ('.$GLOBALS['TYPO3_DB']->quoteStr($this->conf['filters'], 'tx_kesearch_filters').')'; - $where .= $this->cObj->enableFields($table); - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields, $table, $where); - while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { - // Perform overlay on each record - if (is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { - $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( - 'tx_kesearch_filters', - $row, - $GLOBALS['TSFE']->sys_language_content, - $GLOBALS['TSFE']->sys_language_contentOL - ); - } - $this->filtersFromFlexform[$row['uid']] = $row; - } - } - return $this->filtersFromFlexform; - } - - /** - * get optionrecords of given list of option-IDs - * - * @param string $optionList - * @param boolean $returnSortedByTitle Default: Sort by the exact order as they appear in optionlist. This is usefull if the customer want's the same ordering as in the filterRecord (inline) - * @return array Filteroptions - */ - public function getFilterOptions($optionList, $returnSortedByTitle = false) { - // check/convert if list contains only integers - $optionIdArray = GeneralUtility::intExplode(',', $optionList, true); - $optionList = implode(',', $optionIdArray); - if($returnSortedByTitle) { - $sortBy = 'title'; - } else $sortBy = 'FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionList, 'tx_kesearch_filteroptions') . '")'; - - // search for filteroptions - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( - '*', - 'tx_kesearch_filteroptions', - 'pid in ('.$this->startingPoints.') ' . - 'AND FIND_IN_SET(uid, "' . $GLOBALS['TYPO3_DB']->quoteStr($optionList, 'tx_kesearch_filteroptions') . '") ' . - $this->cObj->enableFields('tx_kesearch_filteroptions'), - '', $sortBy, '' - ); - while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { - // Perform overlay on each record - if(is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { - $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( - 'tx_kesearch_filteroptions', - $row, - $GLOBALS['TSFE']->sys_language_content, - $GLOBALS['TSFE']->sys_language_contentOL - ); - } - $optionArray[$row['uid']] = $row; - } - - return $optionArray; - } - - /** - * set the text for "no results" - * - */ - public function setNoResultsText() { - - // no results found - if($this->conf['showNoResultsText']) { - // use individual text set in flexform - $noResultsText = $this->pi_RTEcssText($this->conf['noResultsText']); - } else { - // use general text - $noResultsText = $this->pi_getLL('no_results_found'); - } - - // hook to implement your own idea of a no result message - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['noResultsHandler'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['noResultsHandler'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->noResultsHandler($noResultsText, $this); - } - } - - // fill the fluid template marker - $this->fluidTemplateVariables['noResultsText'] = $noResultsText; - } - - /** - * creates the search result list - * 1. does the actual searching (fetches the results to $rows) - * 2. fills fluid variables for fluid templates to $this->fluidTemplateVariables - * - */ - public function getSearchResults() { - - // fetch the search results - $limit = $this->db->getLimit(); - $rows = $this->db->getSearchResults(); - - // TODO: Check how Sphinx handles this, seems to return full result set - if(count($rows) > $limit[1]) { - $rows = array_slice($rows, $limit[0], $limit[1]); - } - - // set number of results - $this->numberOfResults = $this->db->getAmountOfSearchResults(); - - // count searchword with ke_stats - $this->countSearchWordWithKeStats($this->sword); - - // count search phrase in ke_search statistic tables - if ($this->conf['countSearchPhrases']) { - $this->countSearchPhrase($this->sword, $this->swords, $this->numberOfResults, $this->tagsAgainst); - } - - // render "no results" text and stop here - if ($this->numberOfResults == 0) { - return $this->setNoResultsText(); - } - - // set switch for too short words - $this->fluidTemplateVariables['wordsTooShort'] = $this->hasTooShortWords ? 1 : 0; - - // init counter and loop through the search results - $resultCount = 1; - $this->searchResult = GeneralUtility::makeInstance('tx_kesearch_lib_searchresult', $this); - - $this->fluidTemplateVariables['resultrows'] = array(); - - foreach($rows as $row) { - $this->searchResult->setRow($row); - - $tempMarkerArray = array( - 'orig_uid' => $row['orig_uid'], - 'orig_pid' => $row['orig_pid'], - 'title' => $this->searchResult->getTitle(), - 'teaser' => $this->searchResult->getTeaser(), - ); - - // hook for additional markers in result row - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['additionalResultMarker'])) { - // make curent row number available to hook - $this->currentRowNumber = $resultCount; - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['additionalResultMarker'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->additionalResultMarker($tempMarkerArray, $row, $this); - } - unset($this->currentRowNumber); - } - - // add type marker - // for file results just use the "file" type, not the file extension (eg. "file:pdf") - list($type) = explode(':', $row['type']); - $tempMarkerArray['type'] = str_replace(' ', '_', $type); - - // use the markers array as a base for the fluid template values - $resultrowTemplateValues = $tempMarkerArray; - - // set result url - $resultUrl = $this->searchResult->getResultUrl($this->conf['renderResultUrlAsLink']); - $resultrowTemplateValues['url'] = $resultUrl; - - // set result numeration - $resultNumber = $resultCount + ($this->piVars['page'] * $this->conf['resultsPerPage']) - $this->conf['resultsPerPage']; - $resultrowTemplateValues['number'] = $resultNumber; - - // set score (used for plain score output and score scale) - $resultScore = number_format($row['score'], 2, ',', ''); - $resultrowTemplateValues['score'] = $resultScore; - - // set date (formatted and raw as a timestamp) - $resultDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['sortdate']); - $resultrowTemplateValues['date'] = $resultDate; - $resultrowTemplateValues['date_timestamp'] = $row['sortdate']; - - // set percental score - $resultrowTemplateValues['percent'] = $row['percent']; - - // show tags? - $tags = $row['tags']; - $tags = str_replace('#', ' ', $tags); - $resultrowTemplateValues['tags'] = $tags; - - // set preview image - $renderedImage = $this->renderPreviewImageOrTypeIcon($row); - $resultrowTemplateValues['imageHtml'] = $renderedImage; - - // set end date for cal events - if ($type == 'cal') { - $resultrowTemplateValues['cal'] = $this->getCalEventEnddate($row['orig_uid']); - } - - // add result row to the variables array - $this->fluidTemplateVariables['resultrows'][] = $resultrowTemplateValues; - - // increase result counter - $resultCount++; - } - } - - - /** - * renders a preview image in the result list or a icon which indicates - * the type of the result (page, news, ...) - * - * returns an array with - * index 0: pure HTML code for the image for the use in fluid templating - * index 1: fully rendered subpart for marker based templating - * - * TODO: Move the image rendering itself to fluid - * - * @author Christian Bülter - * @since 19.03.15 - * @return array - */ - function renderPreviewImageOrTypeIcon($row) { - - // preview image (instead of type icon) - list($type, $filetype) = explode(':', $row['type']); - switch ($type) { - - case 'file': - if ($this->conf['showFilePreview']) { - $imageHtml = $this->renderFilePreview($row); - } - break; - - case 'page': - if ($this->conf['showPageImages']) { - $imageHtml = $this->renderFALPreviewImage($row['orig_uid'], 'pages', 'media'); - } - break; - - case 'news': - if ($this->conf['showNewsImages']) { - $imageHtml = $this->renderFALPreviewImage($row['orig_uid'], 'tx_news_domain_model_news', 'fal_media'); - } - break; - - default: - $imageHtml = ''; - break; - } - - // render type icon if no preview image is available (or preview is disabled) - if ($this->conf['showTypeIcon'] && empty($imageHtml)) { - $imageHtml = $this->renderTypeIcon($row['type']); - } - - return $imageHtml; - } - - /** - * Counts searchword and -phrase if ke_stats is installed - * - * @param string $searchphrase - * @return void - * @author Christian Buelter - * @since Tue Mar 01 2011 12:34:25 GMT+0100 - */ - public function countSearchWordWithKeStats($searchphrase='') { - - $searchphrase = trim($searchphrase); - $keStatsIsLoaded = ExtensionManagementUtility::isLoaded('ke_stats'); - if ($keStatsIsLoaded && !empty($searchphrase)) { - $keStatsObj = GeneralUtility::getUserObj('EXT:ke_stats/pi1/class.tx_kestats_pi1.php:tx_kestats_pi1'); - $keStatsObj->initApi(); - - // count words - $wordlist = GeneralUtility::trimExplode(' ', $searchphrase, true); - foreach ($wordlist as $singleword) { - $keStatsObj->increaseCounter( - 'ke_search Words', - 'element_title,year,month', - $singleword, - 0, - $this->firstStartingPoint, - $GLOBALS['TSFE']->sys_page->sys_language_uid, - 0, - 'extension' - ); - } - - // count phrase - $keStatsObj->increaseCounter( - 'ke_search Phrases', - 'element_title,year,month', - $searchphrase, - 0, - $this->firstStartingPoint, - $GLOBALS['TSFE']->sys_page->sys_language_uid, - 0, - 'extension' - ); - - unset($wordlist); - unset($singleword); - unset($keStatsObj); - } - } - - - /** - * Fetches configuration value given its name. - * Merges flexform and TS configuration values. - * - * @param string $param Configuration value name - * @return string Parameter value - */ - public function fetchConfigurationValue($param, $sheet = 'sDEF') { - $value = trim($this->pi_getFFvalue( - $this->cObj->data['pi_flexform'], $param, $sheet) - ); - return $value ? $value : $this->conf[$param]; - } - - - /** - * function betterSubstr - * better substring function - * - * @param $str string - * @param $length integer - * @param $minword integer - * @return string - */ - public function betterSubstr($str, $length = 0, $minword = 3) { - $sub = ''; - $len = 0; - foreach (explode(' ', $str) as $word) { - $part = (($sub != '') ? ' ' : '') . $word; - $sub .= $part; - $len += strlen($part); - if (strlen($word) > $minword && strlen($sub) >= $length) { - break; - } - } - return $sub . (($len < strlen($str)) ? '...' : ''); - } - - - /** - * render parts for the pagebrowser - * @todo do the rendering completely in fluid - */ - public function renderPagebrowser() { - - $numberOfResults = $this->numberOfResults; - $resultsPerPage = $this->conf['resultsPerPage']; - $maxPages = $this->conf['maxPagesInPagebrowser']; - - // get total number of items to show - if ($numberOfResults > $resultsPerPage) { - // show pagebrowser if there are more entries that are - // shown on one page - $this->limit = $resultsPerPage; - } else { - // do not show pagebrowser - return ; - } - - // set db limit - $start = ($this->piVars['page'] * $resultsPerPage) - $resultsPerPage; - $this->dbLimit = $start . ',' . $resultsPerPage; - - // number of pages - $pagesTotal = ceil($numberOfResults/ $resultsPerPage); - - $startPage = $this->piVars['page'] - ceil(($maxPages/2)); - $endPage = $startPage + $maxPages - 1; - if ($startPage < 1) { - $startPage = 1; - $endPage = $startPage + $maxPages -1; - } - if ($startPage > $pagesTotal) { - $startPage = $pagesTotal - $maxPages + 1; - $endPage = $pagesTotal; - } - if ($endPage > $pagesTotal) { - $startPage = $pagesTotal - $maxPages + 1; - $endPage = $pagesTotal; - } - - // render pages list - $tempContent = ''; - $links = []; - $currentPage = intval($startPage/$resultsPerPage) + 1; - for ($i=1; $i<=$pagesTotal; $i++) { - if ($i >= $startPage && $i <= $endPage) { - - // render static version - unset($linkconf); - if ($i === $currentPage) { - $linkconf['class'] = 'current'; - } - $linkconf['parameter'] = $GLOBALS['TSFE']->id; - $linkconf['addQueryString'] = 1; - $linkconf['addQueryString.']['exclude'] = 'id,cHash'; - $linkconf['useCacheHash'] = 1; - $linkconf['additionalParams'] = '&tx_kesearch_pi1[page]=' . intval($i); - - if (is_array($this->piVars['filter'])) { - foreach($this->piVars['filter'] as $filterId => $data) { - if(is_array($data)) { - foreach($data as $tagKey => $tag) { - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . '][' . $tagKey . ']='.$tag; - } - } else $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . ']=' . $this->piVars['filter'][$filterId]; - } - } - - if ($this->piVars['page'] == $i) $linkconf['ATagParams'] = 'class="current" '; - $tempContent .= '
  • ' . $this->cObj->typoLink($i, $linkconf) . '
  • '; - $links[] = $this->cObj->typoLink($i, $linkconf); - } - } - // end - $end = ($start+$resultsPerPage > $numberOfResults) ? $numberOfResults : ($start+$resultsPerPage); - - // previous image with link - if ($this->piVars['page'] > 1) { - - $previousPage = $this->piVars['page'] - 1; - - // get static version - unset($linkconf); - $linkconf['parameter'] = $GLOBALS['TSFE']->id; - $linkconf['addQueryString'] = 1; - $linkconf['addQueryString.']['exclude'] = 'id,cHash'; - $linkconf['useCacheHash'] = 1; - $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' . $this->piVars['sword']; - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[page]=' . intval($previousPage); - - if (is_array($this->piVars['filter'])) { - foreach($this->piVars['filter'] as $filterId => $data) { - if(is_array($data)) { - foreach($data as $tagKey => $tag) { - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . '][' . $tagKey . ']=' . $tag; - } - } else $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . ']=' . $this->piVars['filter'][$filterId]; - } - } - - $linkconf['ATagParams'] = 'class="prev" '; - $links['previous'] = $this->cObj->typoLink($this->pi_getLL('pagebrowser_prev'), $linkconf); - $previous = '
  • ' . $links['previous'] . '
  • '; - } else { - $previous = ''; - } - - // next image with link - if ($this->piVars['page'] < $pagesTotal) { - $nextPage = $this->piVars['page']+1; - - // get static version - unset($linkconf); - $linkconf['parameter'] = $GLOBALS['TSFE']->id; - $linkconf['addQueryString'] = 1; - $linkconf['addQueryString.']['exclude'] = 'id,cHash'; - $linkconf['useCacheHash'] = 1; - $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]='.$this->piVars['sword']; - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[page]='.intval($nextPage); - - if (is_array($this->piVars['filter'])) { - foreach($this->piVars['filter'] as $filterId => $data) { - if(is_array($data)) { - foreach($data as $tagKey => $tag) { - $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . '][' . $tagKey . ']=' . $tag; - } - } else $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filterId . ']=' . $this->piVars['filter'][$filterId]; - } - } - - $linkconf['ATagParams'] = 'class="next" '; - $links['next'] = $this->cObj->typoLink($this->pi_getLL('pagebrowser_next'), $linkconf); - $next = '
  • ' . $links['next'] . '
  • '; - } else { - $next = ''; - } - - // compile previous, pages list and next link into one ul element - $pagebrowser_links = '
      ' . $previous . $tempContent . $next . '
    '; - - - // render pagebrowser content - $markerArray = array( - 'current' => $this->piVars['page'], - 'pages_total' => $pagesTotal, - 'pages_list' => $pagebrowser_links, - 'links' => $links, - 'start' => $start+1, - 'end' => $end, - 'total' => $numberOfResults, - 'results' => $this->pi_getLL('results'), - 'until' => $this->pi_getLL('until'), - 'of' => $this->pi_getLL('of'), - ); - - // hook for additional markers in pagebrowse - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['pagebrowseAdditionalMarker'])) { - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['pagebrowseAdditionalMarker'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->pagebrowseAdditionalMarker( - $markerArray, - $this - ); - } - } - - $this->fluidTemplateVariables['pagebrowser'] = $markerArray; - } - - - public function renderOrdering() { - $sortObj = GeneralUtility::makeInstance('tx_kesearch_lib_sorting', $this); - return $sortObj->renderSorting($this->fluidTemplateVariables); - } - - /** - * renders the preview image of a file result - * - * @param array $row result row - * @author Christian Bülter - * @since 17.10.14 - * @return string - */ - public function renderFilePreview($row) { - list($type, $filetype) = explode(':', $row['type']); - if (in_array($filetype, $this->fileTypesWithPreviewPossible)) { - $imageConf = $this->conf['previewImage.']; - - // if index record is of type "file" and contains an orig_uid, this is the reference - // to a FAL record. Otherwise we use the path directly. - if ($row['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($row['orig_uid']))) { - $metadata = $fileObject->_getMetaData(); - $imageConf['file'] = $fileObject->getPublicUrl(); - $imageConf['altText'] = $metadata['alternative']; - } else { - $imageConf['file'] = $row['directory'] . rawurlencode($row['title']); - } - return $this->renderPreviewImage($imageConf); - } - } - - /** - * renders the preview image of a result which has an attached image, - * needs FAL and is therefore only available for TYPO3 version 6 or higher. - * Returns an empty string if no image could be rendered. - * - * @param integer $uid uid of referencing record - * @param string $table table name of the original table - * @param string $fieldname field which holds the FAL reference - * @author Christian Bülter - * @since 5.11.14 - * @return string - */ - public function renderFALPreviewImage($uid, $table='pages', $fieldname='media') { - $imageHtml = ''; - - $imageConf = $this->conf['previewImage.']; - $fileRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository'); - $fileObjects = $fileRepository->findByRelation($table, $fieldname, $uid); - if (count($fileObjects)) { - $fileObject = $fileObjects[0]; - } - - if ($fileObject) { - $referenceProperties = $fileObject->getReferenceProperties(); - $originalFileProperties = $fileObject->getOriginalFile()->getProperties(); - $alternative = $referenceProperties['alternative'] ? $referenceProperties['alternative'] : $originalFileProperties['alternative']; - - $imageConf['file'] = $fileObject->getPublicUrl(); - $imageConf['altText'] = $alternative; - $imageHtml = $this->renderPreviewImage($imageConf); - } - - return $imageHtml; - } - - /** - * renders a review image and sets the max. width and max. height if not - * defined yet. - * - * @param array $imageConf - * @return string - */ - public function renderPreviewImage($imageConf) { - if (empty($imageConf['file.']['maxW'])) $imageConf['file.']['maxW'] = 150; - if (empty($imageConf['file.']['maxH'])) $imageConf['file.']['maxH'] = 150; - $rendered = $this->cObj->cObjGetSingle('IMAGE', $imageConf); - return $rendered; - } - - /** - * renders an image tag which will prepend the teaser if activated by user. - * - * @param $typeComplete string A value like page, tt_address, for files eg. "file:pdf" - * @return string - */ - public function renderTypeIcon($typeComplete) { - list($type) = explode(':', $typeComplete); - $name = str_replace(':', '_', $typeComplete); - - if ($this->conf['resultListTypeIcon.'][$name . '.']) { - $imageConf = $this->conf['resultListTypeIcon.'][$name . '.']; - } else { - // custom image (old configuration option, only for gif images) - if ($this->conf['additionalPathForTypeIcons']) { - $imageConf['file'] = str_replace(PATH_site, '', GeneralUtility::getFileAbsFileName($this->conf['additionalPathForTypeIcons'] . $name . '.gif')); - } - } - - // fallback: default image - if(!is_file(PATH_site . $imageConf['file'])) { - $imageConf['file'] = ExtensionManagementUtility::siteRelPath($this->extKey) . 'res/img/types/' . $name . '.gif'; - - // fallback for file results: use default if no image for this file extension is available - if($type == 'file' && !is_file(PATH_site . $imageConf['file'])) { - $imageConf['file'] = ExtensionManagementUtility::siteRelPath($this->extKey) . 'res/img/types/file.gif'; - } - } - - $rendered = $this->cObj->cObjGetSingle('IMAGE', $imageConf); - - return $rendered; - } - - /* - * count searchwords and phrases in statistic tables - * assumes that charset ist UTF-8 and uses mb_strtolower - * - * @param $searchPhrase string - * @param $searchWordsArray array - * @param $hits int - * @param $this->tagsAgainst string - * @return void - * - */ - public function countSearchPhrase($searchPhrase, $searchWordsArray, $hits, $tagsAgainst) { - - // prepare "tagsAgainst" - $search = array('"', ' ', '+'); - $replace = array('', '', ''); - $tagsAgainst = str_replace($search, $replace, implode(' ', $tagsAgainst)); - - if (extension_loaded('mbstring')) { - $searchPhrase = mb_strtolower($searchPhrase, 'UTF-8'); - } else { - $searchPhrase = strtolower($searchPhrase); - } - - // count search phrase - if (!empty($searchPhrase)) { - $table = 'tx_kesearch_stat_search'; - $fields_values = array( - 'pid' => $this->firstStartingPoint, - 'searchphrase' => '\'' . $searchPhrase . '\'', - 'tstamp' => time(), - 'hits' => $hits, - 'tagsagainst' => $tagsAgainst, - 'language' => $GLOBALS['TSFE']->sys_language_uid, - ); - $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, array('searchphrase')); - } - - // count single words - foreach ($searchWordsArray as $searchWord) { - if (extension_loaded('mbstring')) { - $searchWord = mb_strtolower($searchWord, 'UTF-8'); - } else { - $searchWord = strtolower($searchWord); - } - $table = 'tx_kesearch_stat_word'; - $timestamp = time(); - if (!empty($searchWord)) { - $fields_values = array( - 'pid' => $this->firstStartingPoint, - 'word' => '\'' . $searchWord . '\'', - 'tstamp' => $timestamp, - 'pageid' => $GLOBALS['TSFE']->id, - 'resultsfound' => $hits ? 1 : 0, - 'language' => $GLOBALS['TSFE']->sys_language_uid, - ); - $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, array('word')); - } - } - } - - - /** - * gets all preselected filters from flexform - * - * @return none but fills global var with needed data - */ - public function getFilterPreselect() { - // get definitions from plugin settings - // and proceed only when preselectedFilter was not set - // this reduces the amount of sql queries, too - if($this->conf['preselected_filters'] && count($this->preselectedFilter) == 0) { - $preselectedArray = GeneralUtility::trimExplode(',', $this->conf['preselected_filters'], true); - foreach ($preselectedArray as $option) { - $option = intval($option); - $fields = ' +class tx_kesearch_lib extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin +{ + // Same as class name + public $prefixId = 'tx_kesearch_pi1'; + + // The extension key. + public $extKey = 'ke_search'; + + // cleaned searchword (karl-heinz => karl heinz) + public $sword = ''; + + // searchwords as array + public $swords = ''; + + // searchphrase for boolean mode (+karl* +heinz*) + public $wordsAgainst = ''; + + // tagsphrase for boolean mode (+#category_213# +#city_42#) + public $tagsAgainst = ''; + + // searchphrase for score/non boolean mode (karl heinz) + public $scoreAgainst = ''; + + // true if no searchparams given; otherwise false + public $isEmptySearch = true; + + // comma seperated list of startingPoints + public $startingPoints = 0; + + // first entry in list of startingpoints + public $firstStartingPoint = 0; + + // FlexForm-Configuration + public $conf = array(); + + // Extension-Configuration + public $extConf = array(); + + // Extension-Configuration of ke_search_premium if installed + public $extConfPremium = array(); + + // count search results + public $numberOfResults = 0; + + // it's for 'USE INDEX ($indexToUse)' to speed up queries + public $indexToUse = ''; + + // contains all tags of current search result + public $tagsInSearchResult = false; + + // preselected filters by flexform + public $preselectedFilter = array(); + + // array with filter-uids as key and whole data as value + public $filtersFromFlexform = array(); + + // contains a boolean value which represents if there are too short words in the searchstring + public $hasTooShortWords = false; + public $fileTypesWithPreviewPossible = array('pdf', 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif', 'tiff'); + public $fluidTemplateVariables = array(); + + /** + * @var tx_xajax + */ + public $xajax; + + /** + * @var tx_kesearch_db + */ + public $db; + + /** + * @var tx_kesearch_lib_div + */ + public $div; + + /** + * @var user_kesearchpremium + */ + public $user_kesearchpremium; + + /** + * @var tx_kesearch_lib_searchresult + */ + public $searchResult; + + /** + * @var tx_kesearch_filters + */ + public $filters; + + /** + * Initializes flexform, conf vars and some more + * @return void + */ + public function init() + { + // get some helper functions + $this->div = GeneralUtility::makeInstance('tx_kesearch_lib_div', $this); + + // set start of query timer + if (!$GLOBALS['TSFE']->register['ke_search_queryStartTime']) { + $GLOBALS['TSFE']->register['ke_search_queryStartTime'] = GeneralUtility::milliseconds(); + } + + // make settings from flexform available in general configuration ($this->conf) + $this->moveFlexFormDataToConf(); + + // in pi2 (the list plugin) fetch the configuration from pi1 (the search + // box plugin) since all the configuration is done there + if (!empty($this->conf['loadFlexformsFromOtherCE'])) { + $data = $this->pi_getRecord('tt_content', intval($this->conf['loadFlexformsFromOtherCE'])); + $this->cObj->data = $data; + $this->moveFlexFormDataToConf(); + } + + // clean piVars + $this->piVars = $this->div->cleanPiVars($this->piVars); + + // get preselected filter from rootline + $this->getFilterPreselect(); + + // add stdWrap properties to each config value (not to arrays) + foreach ($this->conf as $key => $value) { + if (!is_array($this->conf[$key])) { + $this->conf[$key] = $this->cObj->stdWrap($value, $this->conf[$key . '.']); + } + } + + // set some default values (this part have to be after stdWrap!!!) + if (!$this->conf['resultPage']) { + $this->conf['resultPage'] = $GLOBALS['TSFE']->id; + } + if (!isset($this->piVars['page'])) { + $this->piVars['page'] = 1; + } + if (!empty($this->conf['additionalPathForTypeIcons'])) { + $this->conf['additionalPathForTypeIcons'] = rtrim($this->conf['additionalPathForTypeIcons'], '/') . '/'; + } + + // hook: modifyFlexFormData + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFlexFormData'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFlexFormData'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->modifyFlexFormData($this->conf, $this->cObj, $this->piVars); + } + } + + // prepare database object + $this->db = GeneralUtility::makeInstance('tx_kesearch_db', $this); + + // set startingPoints + $this->startingPoints = $this->div->getStartingPoint(); + + // get filter class + $this->filters = GeneralUtility::makeInstance('tx_kesearch_filters'); + + // get extension configuration array + $this->extConf = tx_kesearch_helper::getExtConf(); + $this->extConfPremium = tx_kesearch_helper::getExtConfPremium(); + + // initialize filters + $this->filters->initialize($this); + + // init templating (marker based or fluid) + $this->initTemplate(); + + // get first startingpoint + $this->firstStartingPoint = $this->div->getFirstStartingPoint($this->startingPoints); + + // build words searchphrase + $searchPhrase = GeneralUtility::makeInstance('tx_kesearch_lib_searchphrase'); + $searchPhrase->initialize($this); + $searchWordInformation = $searchPhrase->buildSearchPhrase(); + + // Hook: modifySearchWords + if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifySearchWords'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifySearchWords'] as $classRef) { + $hookObj = GeneralUtility::getUserObj($classRef); + if (method_exists($hookObj, 'modifySearchWords')) { + $hookObj->modifySearchWords($searchWordInformation, $this); + } + } + } + + // set searchword and tag information + $this->sword = $searchWordInformation['sword']; + $this->swords = $searchWordInformation['swords']; + $this->wordsAgainst = $searchWordInformation['wordsAgainst']; + $this->tagsAgainst = $searchWordInformation['tagsAgainst']; + $this->scoreAgainst = $searchWordInformation['scoreAgainst']; + + $this->isEmptySearch = $this->isEmptySearch(); + + // Since sorting for "relevance" in most cases ist the most useful option and + // this sorting option is not available until a searchword is given, make it + // the default sorting after a searchword has been given. + // Set default sorting to "relevance" if the following conditions are true: + // * sorting by user is allowed + // * sorting for "relevance" is allowed (internal: "score") + // * user did not select his own sorting yet + // * a searchword is given + $isInList = GeneralUtility::inList($this->conf['sortByVisitor'], 'score'); + if ($this->conf['showSortInFrontend'] && $isInList && !$this->piVars['sortByField'] && $this->sword) { + $this->piVars['sortByField'] = 'score'; + $this->piVars['sortByDir'] = 'desc'; + } + + // after the searchword is removed, sorting for "score" is not possible + // anymore. So remove this sorting here and put it back to default. + if (!$this->sword && $this->piVars['sortByField'] == 'score') { + unset($this->piVars['sortByField']); + unset($this->piVars['sortByDir']); + } + + // chooseBestIndex is only needed for MySQL-Search. Not for Sphinx + if (!$this->extConfPremium['enableSphinxSearch']) { + // precount results to find the best index + $this->db->chooseBestIndex($this->wordsAgainst, $this->tagsAgainst); + } + + // perform search at this point already if we need to calculate what + // filters to display. + if ($this->conf['checkFilterCondition'] != 'none') { + $this->db->getSearchResults(); + } + + // add cssTag to header if set + $cssFile = $GLOBALS['TSFE']->tmpl->getFileName($this->conf['cssFile']); + if (!empty($cssFile)) { + $GLOBALS['TSFE']->getPageRenderer()->addCssFile($cssFile); + } + } + + /** + * initializes the fluid template + */ + public function initTemplate() + { + // set default template paths + $this->conf['templateRootPath'] = + GeneralUtility::getFileAbsFileName($this->conf['templateRootPath'] + ? $this->conf['templateRootPath'] + : 'EXT:ke_search/Resources/Private/Templates/'); + + $this->conf['partialRootPath'] = + GeneralUtility::getFileAbsFileName($this->conf['partialRootPath'] + ? $this->conf['partialRootPath'] + : 'EXT:ke_search/Resources/Private/Partials/'); + + $this->conf['layoutRootPath'] + = GeneralUtility::getFileAbsFileName($this->conf['layoutRootPath'] + ? $this->conf['layoutRootPath'] + : 'EXT:ke_search/Resources/Private/Layouts/'); + } + + /** + * Move all FlexForm data of current record to conf array + */ + public function moveFlexFormDataToConf() + { + // don't move this to init + $this->pi_initPIflexForm(); + + $piFlexForm = $this->cObj->data['pi_flexform']; + if (is_array($piFlexForm['data'])) { + foreach ($piFlexForm['data'] as $sheetKey => $sheet) { + foreach ($sheet as $lang) { + foreach ($lang as $key => $value) { + // delete current conf value from conf-array + // when FF-Value differs from TS-Conf and FF-Value is not empty + $value = $this->fetchConfigurationValue($key, $sheetKey); + if ($this->conf[$key] != $value && !empty($value)) { + unset($this->conf[$key]); + $this->conf[$key] = $this->fetchConfigurationValue($key, $sheetKey); + } + } + } + } + } + } + + /** + * creates the searchbox + * fills fluid variables for the fluid template to $this->fluidTemplateVariables + * @return void + */ + public function getSearchboxContent() + { + // set page = 1 for every new search + $pageValue = 1; + $this->fluidTemplateVariables['page'] = $pageValue; + + // submit + $this->fluidTemplateVariables['submitAltText'] = $this->pi_getLL('submit'); + + // searchword input value + $searchString = $this->piVars['sword']; + + if (!empty($searchString) && $searchString != $this->pi_getLL('searchbox_default_value')) { + $this->swordValue = $searchString; + } else { + $this->swordValue = ''; + } + + $this->fluidTemplateVariables['searchword'] = htmlspecialchars($this->swordValue); + $this->fluidTemplateVariables['searchwordDefault'] = htmlspecialchars( + $this->pi_getLL('searchbox_default_value') + ); + $this->fluidTemplateVariables['sortByField'] = $this->piVars['sortByField']; + $this->fluidTemplateVariables['sortByDir'] = $this->piVars['sortByDir']; + + // get filters + $renderedFilters = $this->renderFilters(); + $this->fluidTemplateVariables['filter'] = $renderedFilters; + + // set form action pid + $this->fluidTemplateVariables['targetpage'] = $this->conf['resultPage']; + + // set form action + $siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); + $lParam = GeneralUtility::_GET('L'); + $mpParam = GeneralUtility::_GET('MP'); + $typeParam = GeneralUtility::_GP('type'); + $actionUrl = $siteUrl . 'index.php'; + $this->fluidTemplateVariables['actionUrl'] = $actionUrl; + + // language parameter + if (isset($lParam)) { + $hiddenFieldValue = intval($lParam); + $this->fluidTemplateVariables['lparam'] = $hiddenFieldValue; + } + + // mountpoint parameter + if (isset($mpParam)) { + // the only allowed characters in the MP parameter are digits and , and - + $hiddenFieldValue = preg_replace('/[^0-9,-]/', '', $mpParam); + $this->fluidTemplateVariables['mpparam'] = $hiddenFieldValue; + } + + // type param + if ($typeParam) { + $hiddenFieldValue = intval($typeParam); + $this->fluidTemplateVariables['typeparam'] = $hiddenFieldValue; + } + + // set reset link + unset($linkconf); + $linkconf['parameter'] = $this->conf['resultPage']; + $resetUrl = $this->cObj->typoLink_URL($linkconf); + $this->fluidTemplateVariables['resetUrl'] = $resetUrl; + } + + /** + * loop through all available filters and compile the values for the fluid template rendering + + */ + public function renderFilters() + { + foreach ($this->filters->getFilters() as $filter) { + // if the current filter is a "hidden filter", skip + // rendering of this filter. The filter is only used + // to add preselected filter options to the query and + // must not be rendered. + $isInList = GeneralUtility::inList($this->conf['hiddenfilters'], $filter['uid']); + + if ($isInList) { + continue; + } + + // get filter options which should be displayed + $options = $this->findFilterOptionsToDisplay($filter); + + // alphabetical sorting of filter options + if ($filter['alphabeticalsorting'] == 1) { + $this->sortArrayByColumn($options, 'title'); + } + + // hook for modifying filter options + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptionsArray'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptionsArray'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $options = $_procObj->modifyFilterOptionsArray($filter['uid'], $options, $this); + } + } + + // build link to reset this filter while keeping the others + unset($linkconf); + $linkconf['parameter'] = $GLOBALS['TSFE']->id; + $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' . $this->piVars['sword']; + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $filter['uid'] . ']='; + if (is_array($this->piVars['filter']) && count($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $key => $value) { + if ($key != $filter['uid']) { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $key . ']=' . $value; + } + } + } + $resetLink = $this->cObj->typoLink_URL($linkconf); + + // set values for fluid template + $filterData = $filter; + $filterData['name'] = 'tx_kesearch_pi1[filter][' . $filter['uid'] . ']'; + $filterData['id'] = 'filter_' . $filter['uid']; + $filterData['options'] = $options; + $filterData['checkboxOptions'] = $this->compileCheckboxOptions($filter, $options); + $filterData['optionCount'] = count($options); + $filterData['resetLink'] = $resetLink; + + // special classes / custom code + switch ($filter['rendertype']) { + case 'textlinks': + $textLinkObj = GeneralUtility::makeInstance('tx_kesearch_lib_filters_textlinks', $this); + $textLinkObj->renderTextlinks($filter['uid'], $options, $this); + break; + + // use custom code for filter rendering + // set $filterData['rendertype'] = 'custom' + // and $filterData['rawHtmlContent'] to your pre-rendered filter code + default: + // hook for custom filter renderer + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['customFilterRenderer'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['customFilterRenderer'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->customFilterRenderer($filter['uid'], $options, $this, $filterData); + } + } + break; + } + + // add values to fluid template + $this->fluidTemplateVariables['filters'][] = $filterData; + } + } + + /** + * compiles a list of checkbox records + * @param integer $filterUid UID of the filter for which we need the checkboxes + * @param array $options contains all options which are found in the search result + * @return array list of checkboxes records + */ + public function compileCheckboxOptions($filter, $options) + { + $allOptionsOfCurrentFilter = $filter['options']; + + // alphabetical sorting of filter options + if ($filter['alphabeticalsorting'] == 1) { + $this->sortArrayByColumn($allOptionsOfCurrentFilter, 'title'); + } + + // loop through options + $checkboxOptions = array(); + if (is_array($allOptionsOfCurrentFilter)) { + foreach ($allOptionsOfCurrentFilter as $key => $data) { + $data['key'] = $key; + + // check if current option (of searchresults) is in array of all possible options + $isOptionInOptionArray = false; + if (is_array($options)) { + foreach ($options as $optionInResultList) { + if ($optionInResultList['value'] == $data['tag']) { + $isOptionInOptionArray = true; + $data['results'] = $optionInResultList['results']; + break; + } + } + } + + // if option is in optionArray, we have to mark the checkboxes + if ($isOptionInOptionArray) { + // if user has selected a checkbox it must be selected on the resultpage, too. + // options which have been preselected in the backend are + // already in $this->piVars['filter'][$filter['uid]] + if ($this->piVars['filter'][$filter['uid']][$key]) { + $data['selected'] = 1; + } + + // mark all checkboxes if that config options is set and no search string was given and there + // are no preselected filters given for that filter + if ($this->isEmptySearch + && $filter['markAllCheckboxes'] + && empty($this->preselectedFilter[$filter['uid']]) + ) { + $data['selected'] = 1; + } + + } else { // if an option was not found in the search results + $data['disabled'] = 1; + } + + $data['id'] = 'filter_' . $filter['uid'] . '_' . $key; + $checkboxOptions[] = $data; + } + } + + // modify filter options by hook + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->modifyFilterOptions($filter, $checkboxOptions, $this); + } + } + + return $checkboxOptions; + } + + /** + * find out which filter options should be displayed for the given filter + * check filter options availability and preselection status + * @param array $filter + * @return array + * @author Christian Bülter + * @since 09.09.14 + */ + public function findFilterOptionsToDisplay($filter) + { + $options = array(); + + foreach ($filter['options'] as $option) { + // should we check if the filter option is available in + // the current search result? + + if ($this->conf['checkFilterCondition'] != 'none') { + // Once one filter option has been selected, don't display the + // others anymore since this leads to a strange behaviour (options are + // only displayed if they have BOTH tags: the selected and the other filter option. + if ((!count($filter['selectedOptions']) + || in_array($option['uid'], $filter['selectedOptions']) + ) && $this->filters->checkIfTagMatchesRecords($option['tag']) + ) { + // build link which selects this option and keeps all the other selected filters + unset($linkconf); + $linkconf['parameter'] = $GLOBALS['TSFE']->id; + $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' + . $this->piVars['sword'] + . '&tx_kesearch_pi1[filter][' + . $filter['uid'] + . ']=' + . $option['tag']; + $linkconf['useCacheHash'] = false; + if (is_array($this->piVars['filter']) && count($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $key => $value) { + if ($key != $filter['uid']) { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' . $key . ']=' . $value; + } + } + } + $optionLink = $this->cObj->typoLink_URL($linkconf); + + $options[$option['uid']] = array( + 'title' => $option['title'], + 'value' => $option['tag'], + 'results' => $this->tagsInSearchResult[$option['tag']], + 'selected' => is_array($filter['selectedOptions']) + && in_array($option['uid'], $filter['selectedOptions']), + 'link' => $optionLink + ); + } + } else { + // do not process any checks; show all filter options + $options[$option['uid']] = array( + 'title' => $option['title'], + 'value' => $option['tag'], + 'selected' => + is_array($filter['selectedOptions']) + && !empty($filter['selectedOptions']) + && in_array($option['uid'], $filter['selectedOptions']), + ); + } + } + + return $options; + } + + /** + * renders brackets around the number of results, returns an empty + * string if there are no results or if an option for this filter already + * has been selected. + * @param integer $numberOfResults + * @param array $filter + * @return string + */ + public function renderNumberOfResultsString($numberOfResults, $filter) + { + if ($filter['shownumberofresults'] && !count($filter['selectedOptions']) && $numberOfResults) { + $returnValue = ' (' . $numberOfResults . ')'; + } else { + $returnValue = ''; + } + return $returnValue; + } + + /** + * get all filters configured in FlexForm + * @return array Array with filter UIDs + */ + public function getFiltersFromFlexform() + { + if (!empty($this->conf['filters']) && count($this->filtersFromFlexform) == 0) { + $fields = '*'; + $table = 'tx_kesearch_filters'; + $where = 'pid in (' . $GLOBALS['TYPO3_DB']->quoteStr($this->startingPoints, $table) . ')'; + $where .= ' AND uid in (' + . $GLOBALS['TYPO3_DB']->quoteStr($this->conf['filters'], 'tx_kesearch_filters') + . ')'; + $where .= $this->cObj->enableFields($table); + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields, $table, $where); + while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { + // Perform overlay on each record + if (is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { + $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( + 'tx_kesearch_filters', + $row, + $GLOBALS['TSFE']->sys_language_content, + $GLOBALS['TSFE']->sys_language_contentOL + ); + } + $this->filtersFromFlexform[$row['uid']] = $row; + } + } + return $this->filtersFromFlexform; + } + + /** + * get optionrecords of given list of option-IDs + * @param string $optionList + * @param boolean $returnSortedByTitle Default: Sort by the exact order as they appear in + * optionlist. This is usefull if the customer want's the same ordering as in the filterRecord (inline) + * @return array Filteroptions + */ + public function getFilterOptions($optionList, $returnSortedByTitle = false) + { + // check/convert if list contains only integers + $optionIdArray = GeneralUtility::intExplode(',', $optionList, true); + $optionList = implode(',', $optionIdArray); + if ($returnSortedByTitle) { + $sortBy = 'title'; + } else { + $sortBy = 'FIND_IN_SET(uid, "' + . $GLOBALS['TYPO3_DB']->quoteStr($optionList, 'tx_kesearch_filteroptions') + . '")'; + } + + // search for filteroptions + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( + '*', + 'tx_kesearch_filteroptions', + 'pid in (' . $this->startingPoints . ') ' . + 'AND FIND_IN_SET(uid, "' + . $GLOBALS['TYPO3_DB']->quoteStr($optionList, 'tx_kesearch_filteroptions') + . '") ' + . $this->cObj->enableFields('tx_kesearch_filteroptions'), + '', + $sortBy, + '' + ); + + while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { + // Perform overlay on each record + if (is_array($row) && $GLOBALS['TSFE']->sys_language_contentOL) { + $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay( + 'tx_kesearch_filteroptions', + $row, + $GLOBALS['TSFE']->sys_language_content, + $GLOBALS['TSFE']->sys_language_contentOL + ); + } + $optionArray[$row['uid']] = $row; + } + + return $optionArray; + } + + /** + * set the text for "no results" + + */ + public function setNoResultsText() + { + // no results found + if ($this->conf['showNoResultsText']) { + // use individual text set in flexform + $noResultsText = $this->pi_RTEcssText($this->conf['noResultsText']); + } else { + // use general text + $noResultsText = $this->pi_getLL('no_results_found'); + } + + // hook to implement your own idea of a no result message + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['noResultsHandler'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['noResultsHandler'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->noResultsHandler($noResultsText, $this); + } + } + + // fill the fluid template marker + $this->fluidTemplateVariables['noResultsText'] = $noResultsText; + } + + /** + * creates the search result list + * 1. does the actual searching (fetches the results to $rows) + * 2. fills fluid variables for fluid templates to $this->fluidTemplateVariables + + */ + public function getSearchResults() + { + // fetch the search results + $limit = $this->db->getLimit(); + $rows = $this->db->getSearchResults(); + + // TODO: Check how Sphinx handles this, seems to return full result set + if (count($rows) > $limit[1]) { + $rows = array_slice($rows, $limit[0], $limit[1]); + } + + // set number of results + $this->numberOfResults = $this->db->getAmountOfSearchResults(); + + // count searchword with ke_stats + $this->countSearchWordWithKeStats($this->sword); + + // count search phrase in ke_search statistic tables + if ($this->conf['countSearchPhrases']) { + $this->countSearchPhrase($this->sword, $this->swords, $this->numberOfResults, $this->tagsAgainst); + } + + // render "no results" text and stop here + if ($this->numberOfResults == 0) { + return $this->setNoResultsText(); + } + + // set switch for too short words + $this->fluidTemplateVariables['wordsTooShort'] = $this->hasTooShortWords ? 1 : 0; + + // init counter and loop through the search results + $resultCount = 1; + $this->searchResult = GeneralUtility::makeInstance('tx_kesearch_lib_searchresult', $this); + + $this->fluidTemplateVariables['resultrows'] = array(); + + foreach ($rows as $row) { + $this->searchResult->setRow($row); + + $tempMarkerArray = array( + 'orig_uid' => $row['orig_uid'], + 'orig_pid' => $row['orig_pid'], + 'title' => $this->searchResult->getTitle(), + 'teaser' => $this->searchResult->getTeaser(), + ); + + // hook for additional markers in result row + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['additionalResultMarker'])) { + // make curent row number available to hook + $this->currentRowNumber = $resultCount; + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['additionalResultMarker'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->additionalResultMarker($tempMarkerArray, $row, $this); + } + unset($this->currentRowNumber); + } + + // add type marker + // for file results just use the "file" type, not the file extension (eg. "file:pdf") + list($type) = explode(':', $row['type']); + $tempMarkerArray['type'] = str_replace(' ', '_', $type); + + // use the markers array as a base for the fluid template values + $resultrowTemplateValues = $tempMarkerArray; + + // set result url + $resultUrl = $this->searchResult->getResultUrl($this->conf['renderResultUrlAsLink']); + $resultrowTemplateValues['url'] = $resultUrl; + + // set result numeration + $resultNumber = $resultCount + + ($this->piVars['page'] * $this->conf['resultsPerPage']) + - $this->conf['resultsPerPage']; + $resultrowTemplateValues['number'] = $resultNumber; + + // set score (used for plain score output and score scale) + $resultScore = number_format($row['score'], 2, ',', ''); + $resultrowTemplateValues['score'] = $resultScore; + + // set date (formatted and raw as a timestamp) + $resultDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['sortdate']); + $resultrowTemplateValues['date'] = $resultDate; + $resultrowTemplateValues['date_timestamp'] = $row['sortdate']; + + // set percental score + $resultrowTemplateValues['percent'] = $row['percent']; + + // show tags? + $tags = $row['tags']; + $tags = str_replace('#', ' ', $tags); + $resultrowTemplateValues['tags'] = $tags; + + // set preview image + $renderedImage = $this->renderPreviewImageOrTypeIcon($row); + $resultrowTemplateValues['imageHtml'] = $renderedImage; + + // set end date for cal events + if ($type == 'cal') { + $resultrowTemplateValues['cal'] = $this->getCalEventEnddate($row['orig_uid']); + } + + // add result row to the variables array + $this->fluidTemplateVariables['resultrows'][] = $resultrowTemplateValues; + + // increase result counter + $resultCount++; + } + } + + /** + * renders a preview image in the result list or a icon which indicates + * the type of the result (page, news, ...) + * returns an array with + * index 0: pure HTML code for the image for the use in fluid templating + * index 1: fully rendered subpart for marker based templating + * TODO: Move the image rendering itself to fluid + * @author Christian Bülter + * @since 19.03.15 + * @return array + */ + public function renderPreviewImageOrTypeIcon($row) + { + // preview image (instead of type icon) + list($type, $filetype) = explode(':', $row['type']); + switch ($type) { + case 'file': + if ($this->conf['showFilePreview']) { + $imageHtml = $this->renderFilePreview($row); + } + break; + + case 'page': + if ($this->conf['showPageImages']) { + $imageHtml = $this->renderFALPreviewImage($row['orig_uid'], 'pages', 'media'); + } + break; + + case 'news': + if ($this->conf['showNewsImages']) { + $imageHtml = $this->renderFALPreviewImage( + $row['orig_uid'], + 'tx_news_domain_model_news', + 'fal_media' + ); + } + break; + + default: + $imageHtml = ''; + break; + } + + // render type icon if no preview image is available (or preview is disabled) + if ($this->conf['showTypeIcon'] && empty($imageHtml)) { + $imageHtml = $this->renderTypeIcon($row['type']); + } + + return $imageHtml; + } + + /** + * Counts searchword and -phrase if ke_stats is installed + * @param string $searchphrase + * @return void + * @author Christian Buelter + * @since Tue Mar 01 2011 12:34:25 GMT+0100 + */ + public function countSearchWordWithKeStats($searchphrase = '') + { + $searchphrase = trim($searchphrase); + $keStatsIsLoaded = ExtensionManagementUtility::isLoaded('ke_stats'); + if ($keStatsIsLoaded && !empty($searchphrase)) { + $keStatsObj = GeneralUtility::getUserObj('EXT:ke_stats/pi1/class.tx_kestats_pi1.php:tx_kestats_pi1'); + $keStatsObj->initApi(); + + // count words + $wordlist = GeneralUtility::trimExplode(' ', $searchphrase, true); + foreach ($wordlist as $singleword) { + $keStatsObj->increaseCounter( + 'ke_search Words', + 'element_title,year,month', + $singleword, + 0, + $this->firstStartingPoint, + $GLOBALS['TSFE']->sys_page->sys_language_uid, + 0, + 'extension' + ); + } + + // count phrase + $keStatsObj->increaseCounter( + 'ke_search Phrases', + 'element_title,year,month', + $searchphrase, + 0, + $this->firstStartingPoint, + $GLOBALS['TSFE']->sys_page->sys_language_uid, + 0, + 'extension' + ); + + unset($wordlist); + unset($singleword); + unset($keStatsObj); + } + } + + + /** + * Fetches configuration value given its name. + * Merges flexform and TS configuration values. + * @param string $param Configuration value name + * @return string Parameter value + */ + public function fetchConfigurationValue($param, $sheet = 'sDEF') + { + $value = trim( + $this->pi_getFFvalue( + $this->cObj->data['pi_flexform'], + $param, + $sheet + ) + ); + return $value ? $value : $this->conf[$param]; + } + + + /** + * function betterSubstr + * better substring function + * @param $str string + * @param $length integer + * @param $minword integer + * @return string + */ + public function betterSubstr($str, $length = 0, $minword = 3) + { + $sub = ''; + $len = 0; + foreach (explode(' ', $str) as $word) { + $part = (($sub != '') ? ' ' : '') . $word; + $sub .= $part; + $len += strlen($part); + if (strlen($word) > $minword && strlen($sub) >= $length) { + break; + } + } + return $sub . (($len < strlen($str)) ? '...' : ''); + } + + + /** + * render parts for the pagebrowser + * @todo do the rendering completely in fluid + */ + public function renderPagebrowser() + { + + $numberOfResults = $this->numberOfResults; + $resultsPerPage = $this->conf['resultsPerPage']; + $maxPages = $this->conf['maxPagesInPagebrowser']; + + // get total number of items to show + if ($numberOfResults > $resultsPerPage) { + // show pagebrowser if there are more entries that are + // shown on one page + $this->limit = $resultsPerPage; + } else { + // do not show pagebrowser + return; + } + + // set db limit + $start = ($this->piVars['page'] * $resultsPerPage) - $resultsPerPage; + $this->dbLimit = $start . ',' . $resultsPerPage; + + // number of pages + $pagesTotal = ceil($numberOfResults / $resultsPerPage); + + $startPage = $this->piVars['page'] - ceil(($maxPages / 2)); + $endPage = $startPage + $maxPages - 1; + if ($startPage < 1) { + $startPage = 1; + $endPage = $startPage + $maxPages - 1; + } + if ($startPage > $pagesTotal) { + $startPage = $pagesTotal - $maxPages + 1; + $endPage = $pagesTotal; + } + if ($endPage > $pagesTotal) { + $startPage = $pagesTotal - $maxPages + 1; + $endPage = $pagesTotal; + } + + // render pages list + $tempContent = ''; + $links = []; + $currentPage = intval($startPage / $resultsPerPage) + 1; + for ($i = 1; $i <= $pagesTotal; $i++) { + if ($i >= $startPage && $i <= $endPage) { + // render static version + unset($linkconf); + if ($i === $currentPage) { + $linkconf['class'] = 'current'; + } + $linkconf['parameter'] = $GLOBALS['TSFE']->id; + $linkconf['addQueryString'] = 1; + $linkconf['addQueryString.']['exclude'] = 'id,cHash'; + $linkconf['useCacheHash'] = 1; + $linkconf['additionalParams'] = '&tx_kesearch_pi1[page]=' . intval($i); + + if (is_array($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $filterId => $data) { + if (is_array($data)) { + foreach ($data as $tagKey => $tag) { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' + . $filterId + . '][' + . $tagKey + . ']=' + . $tag; + } + } else { + $linkconf['additionalParams'] + .= '&tx_kesearch_pi1[filter][' + . $filterId + . ']=' + . $this->piVars['filter'][$filterId]; + } + } + } + + if ($this->piVars['page'] == $i) { + $linkconf['ATagParams'] = 'class="current" '; + } + $tempContent .= '
  • ' . $this->cObj->typoLink($i, $linkconf) . '
  • '; + $links[] = $this->cObj->typoLink($i, $linkconf); + } + } + // end + $end = ($start + $resultsPerPage > $numberOfResults) ? $numberOfResults : ($start + $resultsPerPage); + + // previous image with link + if ($this->piVars['page'] > 1) { + $previousPage = $this->piVars['page'] - 1; + + // get static version + unset($linkconf); + $linkconf['parameter'] = $GLOBALS['TSFE']->id; + $linkconf['addQueryString'] = 1; + $linkconf['addQueryString.']['exclude'] = 'id,cHash'; + $linkconf['useCacheHash'] = 1; + $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' . $this->piVars['sword']; + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[page]=' . intval($previousPage); + + if (is_array($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $filterId => $data) { + if (is_array($data)) { + foreach ($data as $tagKey => $tag) { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' + . $filterId + . '][' + . $tagKey + . ']=' + . $tag; + } + } else { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' + . $filterId + . ']=' + . $this->piVars['filter'][$filterId]; + } + } + } + + $linkconf['ATagParams'] = 'class="prev" '; + $links['previous'] = $this->cObj->typoLink($this->pi_getLL('pagebrowser_prev'), $linkconf); + $previous = '
  • ' . $links['previous'] . '
  • '; + } else { + $previous = ''; + } + + // next image with link + if ($this->piVars['page'] < $pagesTotal) { + $nextPage = $this->piVars['page'] + 1; + + // get static version + unset($linkconf); + $linkconf['parameter'] = $GLOBALS['TSFE']->id; + $linkconf['addQueryString'] = 1; + $linkconf['addQueryString.']['exclude'] = 'id,cHash'; + $linkconf['useCacheHash'] = 1; + $linkconf['additionalParams'] = '&tx_kesearch_pi1[sword]=' . $this->piVars['sword']; + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[page]=' . intval($nextPage); + + if (is_array($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $filterId => $data) { + if (is_array($data)) { + foreach ($data as $tagKey => $tag) { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' + . $filterId + . '][' + . $tagKey + . ']=' + . $tag; + } + } else { + $linkconf['additionalParams'] .= '&tx_kesearch_pi1[filter][' + . $filterId + . ']=' + . $this->piVars['filter'][$filterId]; + } + } + } + + $linkconf['ATagParams'] = 'class="next" '; + $links['next'] = $this->cObj->typoLink($this->pi_getLL('pagebrowser_next'), $linkconf); + $next = '
  • ' . $links['next'] . '
  • '; + } else { + $next = ''; + } + + // compile previous, pages list and next link into one ul element + $pagebrowser_links = '
      ' . $previous . $tempContent . $next . '
    '; + + + // render pagebrowser content + $markerArray = array( + 'current' => $this->piVars['page'], + 'pages_total' => $pagesTotal, + 'pages_list' => $pagebrowser_links, + 'links' => $links, + 'start' => $start + 1, + 'end' => $end, + 'total' => $numberOfResults, + 'results' => $this->pi_getLL('results'), + 'until' => $this->pi_getLL('until'), + 'of' => $this->pi_getLL('of'), + ); + + // hook for additional markers in pagebrowse + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['pagebrowseAdditionalMarker'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['pagebrowseAdditionalMarker'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->pagebrowseAdditionalMarker( + $markerArray, + $this + ); + } + } + + $this->fluidTemplateVariables['pagebrowser'] = $markerArray; + } + + + public function renderOrdering() + { + $sortObj = GeneralUtility::makeInstance('tx_kesearch_lib_sorting', $this); + return $sortObj->renderSorting($this->fluidTemplateVariables); + } + + /** + * renders the preview image of a file result + * @param array $row result row + * @author Christian Bülter + * @since 17.10.14 + * @return string + */ + public function renderFilePreview($row) + { + list($type, $filetype) = explode(':', $row['type']); + if (in_array($filetype, $this->fileTypesWithPreviewPossible)) { + $imageConf = $this->conf['previewImage.']; + + // if index record is of type "file" and contains an orig_uid, this is the reference + // to a FAL record. Otherwise we use the path directly. + if ($row['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($row['orig_uid']))) { + $metadata = $fileObject->_getMetaData(); + $imageConf['file'] = $fileObject->getPublicUrl(); + $imageConf['altText'] = $metadata['alternative']; + } else { + $imageConf['file'] = $row['directory'] . rawurlencode($row['title']); + } + return $this->renderPreviewImage($imageConf); + } + } + + /** + * renders the preview image of a result which has an attached image, + * needs FAL and is therefore only available for TYPO3 version 6 or higher. + * Returns an empty string if no image could be rendered. + * @param integer $uid uid of referencing record + * @param string $table table name of the original table + * @param string $fieldname field which holds the FAL reference + * @author Christian Bülter + * @since 5.11.14 + * @return string + */ + public function renderFALPreviewImage($uid, $table = 'pages', $fieldname = 'media') + { + $imageHtml = ''; + + $imageConf = $this->conf['previewImage.']; + $fileRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository'); + $fileObjects = $fileRepository->findByRelation($table, $fieldname, $uid); + if (count($fileObjects)) { + $fileObject = $fileObjects[0]; + } + + if ($fileObject) { + $referenceProperties = $fileObject->getReferenceProperties(); + $originalFileProperties = $fileObject->getOriginalFile()->getProperties(); + $alternative = $referenceProperties['alternative'] ? + $referenceProperties['alternative'] : $originalFileProperties['alternative']; + + $imageConf['file'] = $fileObject->getPublicUrl(); + $imageConf['altText'] = $alternative; + $imageHtml = $this->renderPreviewImage($imageConf); + } + + return $imageHtml; + } + + /** + * renders a review image and sets the max. width and max. height if not + * defined yet. + * @param array $imageConf + * @return string + */ + public function renderPreviewImage($imageConf) + { + if (empty($imageConf['file.']['maxW'])) { + $imageConf['file.']['maxW'] = 150; + } + if (empty($imageConf['file.']['maxH'])) { + $imageConf['file.']['maxH'] = 150; + } + $rendered = $this->cObj->cObjGetSingle('IMAGE', $imageConf); + return $rendered; + } + + /** + * renders an image tag which will prepend the teaser if activated by user. + * @param $typeComplete string A value like page, tt_address, for files eg. "file:pdf" + * @return string + */ + public function renderTypeIcon($typeComplete) + { + list($type) = explode(':', $typeComplete); + $name = str_replace(':', '_', $typeComplete); + + if ($this->conf['resultListTypeIcon.'][$name . '.']) { + $imageConf = $this->conf['resultListTypeIcon.'][$name . '.']; + } else { + // custom image (old configuration option, only for gif images) + if ($this->conf['additionalPathForTypeIcons']) { + $imageConf['file'] = str_replace( + PATH_site, + '', + GeneralUtility::getFileAbsFileName($this->conf['additionalPathForTypeIcons'] . $name . '.gif') + ); + } + } + + // fallback: default image + if (!is_file(PATH_site . $imageConf['file'])) { + $imageConf['file'] = ExtensionManagementUtility::siteRelPath($this->extKey) + . 'res/img/types/' . $name . '.gif'; + + // fallback for file results: use default if no image for this file extension is available + if ($type == 'file' && !is_file(PATH_site . $imageConf['file'])) { + $imageConf['file'] = ExtensionManagementUtility::siteRelPath($this->extKey) + . 'res/img/types/file.gif'; + } + } + + $rendered = $this->cObj->cObjGetSingle('IMAGE', $imageConf); + + return $rendered; + } + + /* + * count searchwords and phrases in statistic tables + * assumes that charset ist UTF-8 and uses mb_strtolower + * + * @param $searchPhrase string + * @param $searchWordsArray array + * @param $hits int + * @param $this->tagsAgainst string + * @return void + * + */ + public function countSearchPhrase($searchPhrase, $searchWordsArray, $hits, $tagsAgainst) + { + + // prepare "tagsAgainst" + $search = array('"', ' ', '+'); + $replace = array('', '', ''); + $tagsAgainst = str_replace($search, $replace, implode(' ', $tagsAgainst)); + + if (extension_loaded('mbstring')) { + $searchPhrase = mb_strtolower($searchPhrase, 'UTF-8'); + } else { + $searchPhrase = strtolower($searchPhrase); + } + + // count search phrase + if (!empty($searchPhrase)) { + $table = 'tx_kesearch_stat_search'; + $fields_values = array( + 'pid' => $this->firstStartingPoint, + 'searchphrase' => '\'' . $searchPhrase . '\'', + 'tstamp' => time(), + 'hits' => $hits, + 'tagsagainst' => $tagsAgainst, + 'language' => $GLOBALS['TSFE']->sys_language_uid, + ); + $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, array('searchphrase')); + } + + // count single words + foreach ($searchWordsArray as $searchWord) { + if (extension_loaded('mbstring')) { + $searchWord = mb_strtolower($searchWord, 'UTF-8'); + } else { + $searchWord = strtolower($searchWord); + } + $table = 'tx_kesearch_stat_word'; + $timestamp = time(); + if (!empty($searchWord)) { + $fields_values = array( + 'pid' => $this->firstStartingPoint, + 'word' => '\'' . $searchWord . '\'', + 'tstamp' => $timestamp, + 'pageid' => $GLOBALS['TSFE']->id, + 'resultsfound' => $hits ? 1 : 0, + 'language' => $GLOBALS['TSFE']->sys_language_uid, + ); + $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, array('word')); + } + } + } + + + /** + * gets all preselected filters from flexform + * @return none but fills global var with needed data + */ + public function getFilterPreselect() + { + // get definitions from plugin settings + // and proceed only when preselectedFilter was not set + // this reduces the amount of sql queries, too + if ($this->conf['preselected_filters'] && count($this->preselectedFilter) == 0) { + $preselectedArray = GeneralUtility::trimExplode(',', $this->conf['preselected_filters'], true); + foreach ($preselectedArray as $option) { + $option = intval($option); + $fields = ' tx_kesearch_filters.uid as filteruid, tx_kesearch_filteroptions.uid as optionuid, tx_kesearch_filteroptions.tag '; - $table = 'tx_kesearch_filters, tx_kesearch_filteroptions'; - $where = $GLOBALS['TYPO3_DB']->listQuery('tx_kesearch_filters.options', $option, 'tx_kesearch_filters'); - $where .= ' AND tx_kesearch_filteroptions.uid = ' . $option; - $where .= $this->cObj->enableFields('tx_kesearch_filters'); - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields,$table,$where,$groupBy='',$orderBy='',$limit=''); - while ($row=$GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { - //$this->preselectedFilter[$row['filteruid']][] = $row['tag']; - $this->preselectedFilter[$row['filteruid']][$row['optionuid']] = $row['tag']; - } - } - } - } - - - /** - * function isEmptySearch - * checks if an empty search was loaded / submitted - * - * @return boolean true if no searchparams given; otherwise false - */ - public function isEmptySearch() { - // check if searchword is emtpy or equal with default searchbox value - $emptySearchword = (empty($this->sword) || $this->sword == $this->pi_getLL('searchbox_default_value')) ? true : false; - - // check if filters are set - $filters = $this->filters->getFilters(); - $filterSet = false; - if(is_array($filters)) { - //TODO: piVars filter is a multidimensional array - foreach($filters as $filter) { - if(!empty($this->piVars['filter'][$filter['uid']])) $filterSet = true; - } - } - - if($emptySearchword && !$filterSet) return true; - else return false; - } - - /** - * @param $eventUid - * @return array - */ - public function getCalEventEnddate($eventUid) { - $fields = 'end_date, end_time, allday, start_date'; - $table = 'tx_cal_event'; - $where = 'uid = '.intval($eventUid); - $where .= $this->cObj->enableFields($table); - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields,$table,$where); - $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); - return array( - 'end_timestamp' => strtotime($row['end_date']) + $row['end_time'], - 'end_date' => strtotime($row['end_date']), - 'end_time' => $row['end_time'], - 'allday' => $row['allday'], - 'sameday' => ($row['end_date'] == $row['start_date']) ? 1 : 0 - ); - } - - - /** - * @param array $array - * @param string $field - * @return array - */ - public function sortArrayRecursive($array, $field) { - - $sortArray = Array(); - $mynewArray = Array(); - - $i=1; - foreach ($array as $point) { - $sortArray[] = $point[$field].$i; - $i++; - } - rsort($sortArray); - - foreach ($sortArray as $sortet) { - $i=1; - foreach ($array as $point) { - $newpoint[$field]= $point[$field].$i; - if ($newpoint[$field]==$sortet) $mynewArray[] = $point; - $i++; - } - } - return $mynewArray; - - } - - - /** - * @param array $wert_a - * @param array $wert_b - * @return int - */ - public function sortArrayRecursive2($wert_a, $wert_b) { - // Sortierung nach dem zweiten Wert des Array (Index: 1) - $a = $wert_a[2]; - $b = $wert_b[2]; - - if ($a == $b) { - return 0; - } - - return ($a < $b) ? -1 : +1; - } - - /** - * implements a recursive in_array function - * @param mixed $needle - * @param array $haystack - * @author Christian Bülter - * @since 11.07.12 - * @return bool - */ - public function in_multiarray($needle, $haystack) { - foreach ($haystack as $value) { - if (is_array($value)) { - if ($this->in_multiarray($needle, $value)) { - return true; - } - } else if ($value == $needle) { - return true; - } - } - return false; - } - - /** - * Sort array by given column - * - * @param array $arr the array - * @param string $col the column - * @return void - */ - public function sortArrayByColumn(&$arr, $col) { - - $sort_col = array(); - foreach ($arr as $key => $row) { - $sort_col[$key] = strtoupper($row[$col]); - } - asort($sort_col, SORT_LOCALE_STRING); - - foreach($sort_col as $key => $val) { - $newArray[$key] = $arr[$key]; - } - - $arr = $newArray; - } + $table = 'tx_kesearch_filters, tx_kesearch_filteroptions'; + $where = $GLOBALS['TYPO3_DB']->listQuery('tx_kesearch_filters.options', $option, 'tx_kesearch_filters'); + $where .= ' AND tx_kesearch_filteroptions.uid = ' . $option; + $where .= $this->cObj->enableFields('tx_kesearch_filters'); + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields, $table, $where); + while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { + //$this->preselectedFilter[$row['filteruid']][] = $row['tag']; + $this->preselectedFilter[$row['filteruid']][$row['optionuid']] = $row['tag']; + } + } + } + } + + + /** + * function isEmptySearch + * checks if an empty search was loaded / submitted + * @return boolean true if no searchparams given; otherwise false + */ + public function isEmptySearch() + { + // check if searchword is emtpy or equal with default searchbox value + $emptySearchword = (empty($this->sword) + || $this->sword == $this->pi_getLL('searchbox_default_value')) ? true : false; + + // check if filters are set + $filters = $this->filters->getFilters(); + $filterSet = false; + if (is_array($filters)) { + //TODO: piVars filter is a multidimensional array + foreach ($filters as $filter) { + if (!empty($this->piVars['filter'][$filter['uid']])) { + $filterSet = true; + } + } + } + + if ($emptySearchword && !$filterSet) { + return true; + } else { + return false; + } + } + + /** + * @param $eventUid + * @return array + */ + public function getCalEventEnddate($eventUid) + { + $fields = 'end_date, end_time, allday, start_date'; + $table = 'tx_cal_event'; + $where = 'uid = ' . intval($eventUid); + $where .= $this->cObj->enableFields($table); + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fields, $table, $where); + $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); + return array( + 'end_timestamp' => strtotime($row['end_date']) + $row['end_time'], + 'end_date' => strtotime($row['end_date']), + 'end_time' => $row['end_time'], + 'allday' => $row['allday'], + 'sameday' => ($row['end_date'] == $row['start_date']) ? 1 : 0 + ); + } + + /** + * @param array $array + * @param string $field + * @return array + */ + public function sortArrayRecursive($array, $field) + { + $sortArray = array(); + $mynewArray = array(); + + $i = 1; + foreach ($array as $point) { + $sortArray[] = $point[$field] . $i; + $i++; + } + rsort($sortArray); + + foreach ($sortArray as $sortet) { + $i = 1; + foreach ($array as $point) { + $newpoint[$field] = $point[$field] . $i; + if ($newpoint[$field] == $sortet) { + $mynewArray[] = $point; + } + $i++; + } + } + return $mynewArray; + } + + /** + * @param array $wert_a + * @param array $wert_b + * @return int + */ + public function sortArrayRecursive2($wert_a, $wert_b) + { + // sort using the second value of the array (index: 1) + $a = $wert_a[2]; + $b = $wert_b[2]; + + if ($a == $b) { + return 0; + } + + return ($a < $b) ? -1 : +1; + } + + /** + * implements a recursive in_array function + * @param mixed $needle + * @param array $haystack + * @author Christian Bülter + * @since 11.07.12 + * @return bool + */ + public function in_multiarray($needle, $haystack) + { + foreach ($haystack as $value) { + if (is_array($value)) { + if ($this->in_multiarray($needle, $value)) { + return true; + } + } else { + if ($value == $needle) { + return true; + } + } + } + return false; + } + + /** + * Sort array by given column + * @param array $arr the array + * @param string $col the column + * @return void + */ + public function sortArrayByColumn(&$arr, $col) + { + + $sort_col = array(); + foreach ($arr as $key => $row) { + $sort_col[$key] = strtoupper($row[$col]); + } + asort($sort_col, SORT_LOCALE_STRING); + + foreach ($sort_col as $key => $val) { + $newArray[$key] = $arr[$key]; + } + + $arr = $newArray; + } } + diff --git a/Classes/lib/class.tx_kesearch_lib_div.php b/Classes/lib/class.tx_kesearch_lib_div.php index 59f0c2ee..e2545cea 100644 --- a/Classes/lib/class.tx_kesearch_lib_div.php +++ b/Classes/lib/class.tx_kesearch_lib_div.php @@ -1,187 +1,186 @@ pObj = $pObj; - } - - public function getStartingPoint() { - $startingpoint = array(); - - // if loadFlexformsFromOtherCE is set - // try to get startingPoint of given page - if($uid = intval($this->pObj->conf['loadFlexformsFromOtherCE'])) { - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( - 'pages, recursive', - 'tt_content', - 'uid = ' . $uid, - '', '', '' - ); - if($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { - $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); - $startingpoint['pages'] = $row['pages']; - $startingpoint['recursive'] = $row['recursive']; - } - } else { - // if loadFlexformsFromOtherCE is NOT set - // get startingPoints of current page - $startingpoint['pages'] = $this->pObj->cObj->data['pages']; - $startingpoint['recursive'] = $this->pObj->cObj->data['recursive']; - } - - // allow to override startingpoint with typoscript like this - // plugin.tx_kesearch_pi1.overrideStartingPoint = 123 - // plugin.tx_kesearch_pi1.overrideStartingPointRecursive = 1 - if ($this->pObj->conf['overrideStartingPoint']) { - $startingpoint['pages'] = $this->pObj->conf['overrideStartingPoint']; - $startingpoint['recursive'] = $this->pObj->conf['overrideStartingPointRecursive']; - } - - return $this->pObj->pi_getPidList($startingpoint['pages'], $startingpoint['recursive']); - } - - /** - * Get the first page of starting points - * @param string comma seperated list of page-uids - * @return int first page uid - */ - public function getFirstStartingPoint($pages = 0) { - $pageArray = explode(',', $pages); - return intval($pageArray[0]); - } - - - /** - * Use removeXSS function from t3lib_div / GeneralUtility - * that function exists in the TYPO3 Core at least since version 4.5, - * which is the minimum system requirement for ke_search currentliy (07 / 2015) - * - * @param string value - * @return string XSS safe value - */ - public function removeXSS($value) { - $returnValue = TYPO3\CMS\Core\Utility\GeneralUtility::removeXSS($value); - return $returnValue; - } - - - /** - * function cleanPiVars - * - * cleans all piVars used in this EXT - * uses removeXSS(...), htmlspecialchars(...) and / or intval(...) - * - * @param $piVars array array containing all piVars - * - */ - public function cleanPiVars($piVars) { - - // run through all piVars - foreach ($piVars as $key => $value) { - - // process removeXSS(...) for all piVars - if(!is_array($piVars[$key])) $piVars[$key] = $this->removeXSS($value); - - // process further cleaning regarding to param type - switch ($key) { - - // integer - default 1 - case 'page': - $piVars[$key] = intval($value); - // set to "1" if no value set - if (!$piVars[$key]) $piVars[$key] = 1; - break; - - // integer - case 'resetFilters': - $piVars[$key] = intval($value); - break; - - // array of strings. Defined in the TYPO3 backend - // and posted as piVar. Should not contain any special - // chars (<>"), but just to make sure we remove them here. - case 'filter': - if(is_array($piVars[$key])) { - foreach($piVars[$key] as $filterId => $filterValue) { - if(is_array($piVars[$key][$filterId])) { - foreach($piVars[$key][$filterId] as $key => $value) { - $piVars[$key][$filterId][$key] = htmlspecialchars($value, ENT_QUOTES); - } - } else { - if($piVars[$key][$filterId] != NULL) { - $piVars[$key][$filterId] = htmlspecialchars($filterValue, ENT_QUOTES); - } - } - } - } - break; - - // string, no further XSS cleaning here (except removeXSS, - // see above), cleaning is done on output - case 'sword': - $piVars[$key] = trim($piVars[$key]); - break; - - // only characters - case 'sortByField': - case 'orderByField': - $piVars[$key] = preg_replace('/[^a-zA-Z0-9]/', '', $piVars[$key]); - break; - - // "asc" or "desc" - case 'sortByDir': - case 'orderByDir': - if ($piVars[$key] != 'asc' && $piVars[$key] != 'desc') { - $piVars[$key] = 'asc'; - } - break; - - // remove all other piVars - default: - unset($piVars[$key]); - break; - } - } - - // return cleaned piVars values - return $piVars; - } -} \ No newline at end of file +class tx_kesearch_lib_div +{ + public $showShortMessage = false; + + /** + * Contains the parent object + * @var tx_kesearch_pi1 + */ + public $pObj; + + public function __construct($pObj) + { + $this->pObj = $pObj; + } + + public function getStartingPoint() + { + $startingpoint = array(); + + // if loadFlexformsFromOtherCE is set + // try to get startingPoint of given page + if ($uid = intval($this->pObj->conf['loadFlexformsFromOtherCE'])) { + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( + 'pages, recursive', + 'tt_content', + 'uid = ' . $uid + ); + if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { + $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); + $startingpoint['pages'] = $row['pages']; + $startingpoint['recursive'] = $row['recursive']; + } + } else { + // if loadFlexformsFromOtherCE is NOT set + // get startingPoints of current page + $startingpoint['pages'] = $this->pObj->cObj->data['pages']; + $startingpoint['recursive'] = $this->pObj->cObj->data['recursive']; + } + + // allow to override startingpoint with typoscript like this + // plugin.tx_kesearch_pi1.overrideStartingPoint = 123 + // plugin.tx_kesearch_pi1.overrideStartingPointRecursive = 1 + if ($this->pObj->conf['overrideStartingPoint']) { + $startingpoint['pages'] = $this->pObj->conf['overrideStartingPoint']; + $startingpoint['recursive'] = $this->pObj->conf['overrideStartingPointRecursive']; + } + + return $this->pObj->pi_getPidList($startingpoint['pages'], $startingpoint['recursive']); + } + + /** + * Get the first page of starting points + * + * @param string comma seperated list of page-uids + * @return int first page uid + */ + public function getFirstStartingPoint($pages = '') + { + $pageArray = explode(',', $pages); + return intval($pageArray[0]); + } + + /** + * Use removeXSS function from t3lib_div / GeneralUtility + * that function exists in the TYPO3 Core at least since version 4.5, + * which is the minimum system requirement for ke_search currentliy (07 / 2015) + * + * @param string value + * @return string XSS safe value + */ + public function removeXSS($value) + { + $returnValue = TYPO3\CMS\Core\Utility\GeneralUtility::removeXSS($value); + return $returnValue; + } + + + /** + * function cleanPiVars + * cleans all piVars used in this EXT + * uses removeXSS(...), htmlspecialchars(...) and / or intval(...) + * + * @param $piVars array array containing all piVars + * @return mixed + */ + public function cleanPiVars($piVars) + { + // run through all piVars + foreach ($piVars as $key => $value) { + // process removeXSS(...) for all piVars + if (!is_array($piVars[$key])) { + $piVars[$key] = $this->removeXSS($value); + } + + // process further cleaning regarding to param type + switch ($key) { + // integer - default 1 + case 'page': + $piVars[$key] = intval($value); + // set to "1" if no value set + if (!$piVars[$key]) { + $piVars[$key] = 1; + } + break; + + // integer + case 'resetFilters': + $piVars[$key] = intval($value); + break; + + // array of strings. Defined in the TYPO3 backend + // and posted as piVar. Should not contain any special + // chars (<>"), but just to make sure we remove them here. + case 'filter': + if (is_array($piVars[$key])) { + foreach ($piVars[$key] as $filterId => $filterValue) { + if (is_array($piVars[$key][$filterId])) { + foreach ($piVars[$key][$filterId] as $key => $value) { + $piVars[$key][$filterId][$key] = htmlspecialchars($value, ENT_QUOTES); + } + } else { + if ($piVars[$key][$filterId] != null) { + $piVars[$key][$filterId] = htmlspecialchars($filterValue, ENT_QUOTES); + } + } + } + } + break; + + // string, no further XSS cleaning here (except removeXSS, + // see above), cleaning is done on output + case 'sword': + $piVars[$key] = trim($piVars[$key]); + break; + + // only characters + case 'sortByField': + case 'orderByField': + $piVars[$key] = preg_replace('/[^a-zA-Z0-9]/', '', $piVars[$key]); + break; + + // "asc" or "desc" + case 'sortByDir': + case 'orderByDir': + if ($piVars[$key] != 'asc' && $piVars[$key] != 'desc') { + $piVars[$key] = 'asc'; + } + break; + + // remove all other piVars + default: + unset($piVars[$key]); + break; + } + } + + // return cleaned piVars values + return $piVars; + } +} diff --git a/Classes/lib/class.tx_kesearch_lib_fileinfo.php b/Classes/lib/class.tx_kesearch_lib_fileinfo.php index 1546f43f..48d11612 100644 --- a/Classes/lib/class.tx_kesearch_lib_fileinfo.php +++ b/Classes/lib/class.tx_kesearch_lib_fileinfo.php @@ -1,303 +1,297 @@ setFileInformations($file); - } + /** + * set a filename to get informations of + * @param string|\TYPO3\CMS\Core\Resource\File|\TYPO3\CMS\Core\Resource\FileReference $file + * @return boolean is valid file + */ + public function setFile($file) + { + return $this->setFileInformations($file); + } - /** - * collect all fileinformations of given file and - * save them to the global fileinformation array - * - * @param string $file - * @return boolean is valid file? - */ - protected function setFileInformations($file) { - $this->fileInfo = array(); // reset previously information to have a cleaned object - if ($file instanceof \TYPO3\CMS\Core\Resource\File) { - $this->file = $file; - } - elseif ($file instanceof \TYPO3\CMS\Core\Resource\FileReference) { - $this->file = $file = $file->getOriginalFile(); - } + /** + * collect all fileinformations of given file and + * save them to the global fileinformation array + * @param string $file + * @return boolean is valid file? + */ + protected function setFileInformations($file) + { + $this->fileInfo = array(); // reset previously information to have a cleaned object + if ($file instanceof \TYPO3\CMS\Core\Resource\File) { + $this->file = $file; + } elseif ($file instanceof \TYPO3\CMS\Core\Resource\FileReference) { + $this->file = $file = $file->getOriginalFile(); + } - if(is_string($file) && !empty($file)) { - $this->fileInfo = TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($file); - $this->fileInfo['mtime'] = filemtime($file); - $this->fileInfo['atime'] = fileatime($file); - $this->fileInfo['owner'] = fileowner($file); - $this->fileInfo['group'] = filegroup($file); - $this->fileInfo['size'] = filesize($file); - $this->fileInfo['type'] = filetype($file); - $this->fileInfo['perms'] = fileperms($file); - $this->fileInfo['is_dir'] = is_dir($file); - $this->fileInfo['is_file'] = is_file($file); - $this->fileInfo['is_link'] = is_link($file); - $this->fileInfo['is_readable'] = is_readable($file); - $this->fileInfo['is_uploaded'] = is_uploaded_file($file); - $this->fileInfo['is_writeable'] = is_writeable($file); - } + if (is_string($file) && !empty($file)) { + $this->fileInfo = TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($file); + $this->fileInfo['mtime'] = filemtime($file); + $this->fileInfo['atime'] = fileatime($file); + $this->fileInfo['owner'] = fileowner($file); + $this->fileInfo['group'] = filegroup($file); + $this->fileInfo['size'] = filesize($file); + $this->fileInfo['type'] = filetype($file); + $this->fileInfo['perms'] = fileperms($file); + $this->fileInfo['is_dir'] = is_dir($file); + $this->fileInfo['is_file'] = is_file($file); + $this->fileInfo['is_link'] = is_link($file); + $this->fileInfo['is_readable'] = is_readable($file); + $this->fileInfo['is_uploaded'] = is_uploaded_file($file); + $this->fileInfo['is_writeable'] = is_writeable($file); + } - if ($file instanceof \TYPO3\CMS\Core\Resource\File) { - $pathInfo = \TYPO3\CMS\Core\Utility\PathUtility::pathinfo($file->getName()); - $this->fileInfo = array( - 'file' => $file->getName(), - 'filebody' => $file->getNameWithoutExtension(), - 'fileext' => $file->getExtension(), - 'realFileext' => $pathInfo['extension'], - 'atime' => $file->getCreationTime(), - 'mtime' => $file->getModificationTime(), - 'owner' => '', - 'group' => '', - 'size' => $file->getSize(), - 'type' => 'file', - 'perms' => '', - 'is_dir' => FALSE, - 'is_file' => ($file->getStorage()->getDriverType() === 'Local' ? is_file($file->getForLocalProcessing(FALSE)) : TRUE), - 'is_link' => ($file->getStorage()->getDriverType() === 'Local' ? is_link($file->getForLocalProcessing(FALSE)) : FALSE), - 'is_readable' => TRUE, - 'is_uploaded' => FALSE, - 'is_writeable' => FALSE, - ); - } - return $this->fileInfo !== array(); - } + if ($file instanceof \TYPO3\CMS\Core\Resource\File) { + $pathInfo = \TYPO3\CMS\Core\Utility\PathUtility::pathinfo($file->getName()); + $this->fileInfo = array( + 'file' => $file->getName(), + 'filebody' => $file->getNameWithoutExtension(), + 'fileext' => $file->getExtension(), + 'realFileext' => $pathInfo['extension'], + 'atime' => $file->getCreationTime(), + 'mtime' => $file->getModificationTime(), + 'owner' => '', + 'group' => '', + 'size' => $file->getSize(), + 'type' => 'file', + 'perms' => '', + 'is_dir' => false, + 'is_file' => ($file->getStorage()->getDriverType() === 'Local' ? + is_file($file->getForLocalProcessing(false)) : true), + 'is_link' => ($file->getStorage()->getDriverType() === 'Local' ? + is_link($file->getForLocalProcessing(false)) : false), + 'is_readable' => true, + 'is_uploaded' => false, + 'is_writeable' => false, + ); + } + return $this->fileInfo !== array(); + } - /** - * return relative to site root file path - * - * @return string Filepath (f.e. fileadmin/user_upload/) - */ - public function getRelativePath() { - if ($this->file !== NULL) { - return dirname($this->file->getPublicUrl()) . '/'; - } else { - return str_replace(PATH_site, '', $this->fileInfo['path']); - } - } + /** + * return relative to site root file path + * @return string Filepath (f.e. fileadmin/user_upload/) + */ + public function getRelativePath() + { + if ($this->file !== null) { + return dirname($this->file->getPublicUrl()) . '/'; + } else { + return str_replace(PATH_site, '', $this->fileInfo['path']); + } + } - /** - * return file path - * - * @return string Filepath (f.e. /var/www/fileadmin/) - */ - public function getPath() { - if ($this->file !== NULL) { - return $this->file->getForLocalProcessing(FALSE); - } else { - return $this->fileInfo['path']; - } - } + /** + * return file path + * @return string Filepath (f.e. /var/www/fileadmin/) + */ + public function getPath() + { + if ($this->file !== null) { + return $this->file->getForLocalProcessing(false); + } else { + return $this->fileInfo['path']; + } + } - /** - * return file name - * - * @return string Filename (f.e. Bericht von Bernd.PDF) - */ - public function getName() { - return $this->fileInfo['file']; - } + /** + * return file name + * @return string Filename (f.e. Bericht von Bernd.PDF) + */ + public function getName() + { + return $this->fileInfo['file']; + } - /** - * return file body - * - * @return string Filebody (f.e. Bericht von Bernd) - */ - public function getBody() { - return $this->fileInfo['filebody']; - } + /** + * return file body + * @return string Filebody (f.e. Bericht von Bernd) + */ + public function getBody() + { + return $this->fileInfo['filebody']; + } - /** - * return file extension - * - * @return string lowercased Fileextension (f.e. pdf or xml) - */ - public function getExtension() { - return $this->fileInfo['fileext']; - } + /** + * return file extension + * @return string lowercased Fileextension (f.e. pdf or xml) + */ + public function getExtension() + { + return $this->fileInfo['fileext']; + } - /** - * return the real file extension - * - * @return string The real file extension (f.e. Xml or PDf or DOC) - */ - public function getRealExtension() { - return $this->fileInfo['realFileext']; - } + /** + * return the real file extension + * @return string The real file extension (f.e. Xml or PDf or DOC) + */ + public function getRealExtension() + { + return $this->fileInfo['realFileext']; + } - /** - * return files modification time - * - * @return string Modification time as TimeStamp - */ - public function getModificationTime() { - return $this->fileInfo['mtime']; - } + /** + * return files modification time + * @return string Modification time as TimeStamp + */ + public function getModificationTime() + { + return $this->fileInfo['mtime']; + } - /** - * return files last access time - * - * @return string Last Access time as TimeStamp - */ - public function getLastAccessTime() { - return $this->fileInfo['atime']; - } + /** + * return files last access time + * @return string Last Access time as TimeStamp + */ + public function getLastAccessTime() + { + return $this->fileInfo['atime']; + } - /** - * return files owner - * - * @return string Owner - */ - public function getOwner() { - return $this->fileInfo['owner']; - } + /** + * return files owner + * @return string Owner + */ + public function getOwner() + { + return $this->fileInfo['owner']; + } - /** - * return files group - * - * @return string Group - */ - public function getGroup() { - return $this->fileInfo['group']; - } + /** + * return files group + * @return string Group + */ + public function getGroup() + { + return $this->fileInfo['group']; + } - /** - * return filesize - * - * @return string Filesize - */ - public function getSize() { - return $this->fileInfo['size']; - } + /** + * return filesize + * @return string Filesize + */ + public function getSize() + { + return $this->fileInfo['size']; + } - /** - * return filetype - * - * @return string Filetype - */ - public function getType() { - return $this->fileInfo['type']; - } + /** + * return filetype + * @return string Filetype + */ + public function getType() + { + return $this->fileInfo['type']; + } - /** - * return file permissions - * - * @return string Filepermissions - */ - public function getPermissions() { - return $this->fileInfo['perms']; - } + /** + * return file permissions + * @return string Filepermissions + */ + public function getPermissions() + { + return $this->fileInfo['perms']; + } - /** - * return if file is a directory - * - * @return boolean is file a directory - */ - public function getIsDirectory() { - return $this->fileInfo['is_dir']; - } + /** + * return if file is a directory + * @return boolean is file a directory + */ + public function getIsDirectory() + { + return $this->fileInfo['is_dir']; + } - /** - * return if file is a file - * - * @return boolean is file a file - */ - public function getIsFile() { - return $this->fileInfo['is_file']; - } + /** + * return if file is a file + * @return boolean is file a file + */ + public function getIsFile() + { + return $this->fileInfo['is_file']; + } - /** - * return if file is a (sym)link - * - * @return boolean is file a (sym)link - */ - public function getIsSymLink() { - return $this->fileInfo['is_link']; - } + /** + * return if file is a (sym)link + * @return boolean is file a (sym)link + */ + public function getIsSymLink() + { + return $this->fileInfo['is_link']; + } - /** - * return if file is readable - * - * @return boolean is file readable - */ - public function getIsReadable() { - return $this->fileInfo['is_readable']; - } + /** + * return if file is readable + * @return boolean is file readable + */ + public function getIsReadable() + { + return $this->fileInfo['is_readable']; + } - /** - * return if file is uploaded - * - * @return boolean is file an uploaded file from POST/GET - */ - public function getIsUploaded() { - return $this->fileInfo['is_uploaded']; - } + /** + * return if file is uploaded + * @return boolean is file an uploaded file from POST/GET + */ + public function getIsUploaded() + { + return $this->fileInfo['is_uploaded']; + } - /** - * return if file is writable - * - * @return boolean is file writeable - */ - public function getIsWriteable() { - return $this->fileInfo['is_writeable']; - } + /** + * return if file is writable + * @return boolean is file writeable + */ + public function getIsWriteable() + { + return $this->fileInfo['is_writeable']; + } - /** - * return all fileinformations - * - * @return array all file informations - */ - public function getAllFileInformations() { - return $this->fileInfo; - } + /** + * return all fileinformations + * @return array all file informations + */ + public function getAllFileInformations() + { + return $this->fileInfo; + } } diff --git a/Classes/lib/class.tx_kesearch_lib_helper.php b/Classes/lib/class.tx_kesearch_lib_helper.php index a8b73a8e..d30b51e6 100644 --- a/Classes/lib/class.tx_kesearch_lib_helper.php +++ b/Classes/lib/class.tx_kesearch_lib_helper.php @@ -1,28 +1,24 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ + * Copyright notice + * (c) 2014 Christian Bülter (kennziffer.com) + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ use TYPO3\CMS\Core\Resource\ResourceFactory; +use \TYPO3\CMS\Backend\Utility\BackendUtility; /** * helper functions @@ -30,227 +26,234 @@ * Example: * $this->extConf = tx_kesearch_helper::getExtConf(); */ -class tx_kesearch_helper { +class tx_kesearch_helper +{ - /** - * get extension manager configuration for ke_search - * and make it possible to override it with page ts setup - * - * @author Christian Bülter - * @since 14.10.14 - * @return array - */ - public static function getExtConf() { - $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); + /** + * get extension manager configuration for ke_search + * and make it possible to override it with page ts setup + * @author Christian Bülter + * @since 14.10.14 + * @return array + */ + public static function getExtConf() + { + $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); - // Set the "tagChar" - // sphinx has problems with # in query string. - // so you we need to change the default char # against something else. - // MySQL has problems also with # - // but we wrap # with " and it works. - $keSearchPremiumIsLoaded = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('ke_search_premium'); - if ($keSearchPremiumIsLoaded) { - $extConfPremium = tx_kesearch_helper::getExtConfPremium(); - $extConf['prePostTagChar'] = $extConfPremium['prePostTagChar']; - } else { - $extConf['prePostTagChar'] = '#'; - } - $extConf['multiplyValueToTitle'] = ($extConf['multiplyValueToTitle']) ? $extConf['multiplyValueToTitle'] : 1; - $extConf['searchWordLength'] = ($extConf['searchWordLength']) ? $extConf['searchWordLength'] : 4; + // Set the "tagChar" + // sphinx has problems with # in query string. + // so you we need to change the default char # against something else. + // MySQL has problems also with # + // but we wrap # with " and it works. + $keSearchPremiumIsLoaded = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('ke_search_premium'); + if ($keSearchPremiumIsLoaded) { + $extConfPremium = tx_kesearch_helper::getExtConfPremium(); + $extConf['prePostTagChar'] = $extConfPremium['prePostTagChar']; + } else { + $extConf['prePostTagChar'] = '#'; + } + $extConf['multiplyValueToTitle'] = ($extConf['multiplyValueToTitle']) ? $extConf['multiplyValueToTitle'] : 1; + $extConf['searchWordLength'] = ($extConf['searchWordLength']) ? $extConf['searchWordLength'] : 4; - // override extConf with TS Setup - if (is_array($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.']) && count($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.'])) { - foreach ($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.'] as $key => $value) { - $extConf[$key] = $value; - } - } + // override extConf with TS Setup + if (is_array($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.']) + && count($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.'])) { + foreach ($GLOBALS['TSFE']->tmpl->setup['ke_search.']['extconf.']['override.'] as $key => $value) { + $extConf[$key] = $value; + } + } - return $extConf; - } + return $extConf; + } - /** - * get extension manager configuration for ke_search_premium - * and make it possible to override it with page ts setup - * - * @return array - * @author Christian Bülter - * @since 14.10.14 - */ - public static function getExtConfPremium() { - $keSearchPremiumIsLoaded = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('ke_search_premium'); - if ($keSearchPremiumIsLoaded) { - $extConfPremium = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search_premium']); - if (!$extConfPremium['prePostTagChar']) $extConfPremium['prePostTagChar'] = '_'; - } else { - $extConfPremium = array(); - } + /** + * get extension manager configuration for ke_search_premium + * and make it possible to override it with page ts setup + * @return array + * @author Christian Bülter + * @since 14.10.14 + */ + public static function getExtConfPremium() + { + $keSearchPremiumIsLoaded = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('ke_search_premium'); + if ($keSearchPremiumIsLoaded) { + $extConfPremium = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search_premium']); + if (!$extConfPremium['prePostTagChar']) { + $extConfPremium['prePostTagChar'] = '_'; + } + } else { + $extConfPremium = array(); + } - // override extConfPremium with TS Setup - if (is_array($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.']) && count($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.'])) { - foreach ($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.'] as $key => $value) { - $extConfPremium[$key] = $value; - } - } + // override extConfPremium with TS Setup + if (is_array($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.']) + && count($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.'])) { + foreach ($GLOBALS['TSFE']->tmpl->setup['ke_search_premium.']['extconf.']['override.'] as $key => $value) { + $extConfPremium[$key] = $value; + } + } - return $extConfPremium; - } + return $extConfPremium; + } - /** - * returns the list of assigned categories to a certain record in a certain table - * - * @param integer $uid - * @param string $table - * @author Christian Bülter - * @since 17.10.14 - * @return array - */ - public static function getCategories($uid, $table) { - $categoryData = array( - 'uid_list' => array(), - 'title_list' => array() - ); + /** + * returns the list of assigned categories to a certain record in a certain table + * @param integer $uid + * @param string $table + * @author Christian Bülter + * @since 17.10.14 + * @return array + */ + public static function getCategories($uid, $table) + { + $categoryData = array( + 'uid_list' => array(), + 'title_list' => array() + ); - if ($uid && $table) { - $enableFields = \TYPO3\CMS\Backend\Utility\BackendUtility::BEenableFields('sys_category') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_category'); - $resCat = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query( - 'sys_category.uid, sys_category.title', - 'sys_category', - 'sys_category_record_mm', - $table, - ' AND ' . $table . '.uid = ' . $uid . - ' AND sys_category_record_mm.tablenames = "' . $table . '"' . - $enableFields, - '', - 'sys_category_record_mm.sorting' - ); + if ($uid && $table) { + $enableFields = BackendUtility::BEenableFields('sys_category') + . BackendUtility::deleteClause('sys_category'); + $resCat = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query( + 'sys_category.uid, sys_category.title', + 'sys_category', + 'sys_category_record_mm', + $table, + ' AND ' . $table . '.uid = ' . $uid . + ' AND sys_category_record_mm.tablenames = "' . $table . '"' . + $enableFields, + '', + 'sys_category_record_mm.sorting' + ); - if ($GLOBALS['TYPO3_DB']->sql_num_rows($resCat)) { - while (($cat = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resCat))) { - $categoryData['uid_list'][] = $cat['uid']; - $categoryData['title_list'][] = $cat['title']; - } - } - } + if ($GLOBALS['TYPO3_DB']->sql_num_rows($resCat)) { + while (($cat = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($resCat))) { + $categoryData['uid_list'][] = $cat['uid']; + $categoryData['title_list'][] = $cat['title']; + } + } + } - return $categoryData; - } + return $categoryData; + } - /** - * creates tags from category titles - * removes characters: # , space ( ) _ - * - * @param string $tags comma-list of tags, new tags will be added to this - * @param array $categoryArray Array of Titles (eg. categories) - * @author Christian Bülter - * @since 17.10.14 - */ - public static function makeTags(&$tags, $categoryArray) { - if (is_array($categoryArray) && count($categoryArray)) { - $extConf = tx_kesearch_helper::getExtConf(); + /** + * creates tags from category titles + * removes characters: # , space ( ) _ + * @param string $tags comma-list of tags, new tags will be added to this + * @param array $categoryArray Array of Titles (eg. categories) + * @author Christian Bülter + * @since 17.10.14 + */ + public static function makeTags(&$tags, $categoryArray) + { + if (is_array($categoryArray) && count($categoryArray)) { + $extConf = tx_kesearch_helper::getExtConf(); - foreach ($categoryArray as $catTitle) { - $tag = $catTitle; - $tag = str_replace('#', '', $tag); - $tag = str_replace(',', '', $tag); - $tag = str_replace(' ', '', $tag); - $tag = str_replace('(', '', $tag); - $tag = str_replace(')', '', $tag); - $tag = str_replace('_', '', $tag); - $tag = str_replace('&', '', $tag); + foreach ($categoryArray as $catTitle) { + $tag = $catTitle; + $tag = str_replace('#', '', $tag); + $tag = str_replace(',', '', $tag); + $tag = str_replace(' ', '', $tag); + $tag = str_replace('(', '', $tag); + $tag = str_replace(')', '', $tag); + $tag = str_replace('_', '', $tag); + $tag = str_replace('&', '', $tag); - if (!empty($tags)) { - $tags .= ','; - } - $tags .= $extConf['prePostTagChar'] . $tag . $extConf['prePostTagChar']; - } - } - } + if (!empty($tags)) { + $tags .= ','; + } + $tags .= $extConf['prePostTagChar'] . $tag . $extConf['prePostTagChar']; + } + } + } - /** - * finds the system categories for $uid in $tablename, creates - * tags like "syscat123" ("syscat" + category uid). - * - * @param string $tags - * @param integer $uid - * @param string $tablename - * @author Christian Bülter - * @since 24.09.15 - */ - public static function makeSystemCategoryTags(&$tags, $uid, $tablename) { - $categories = tx_kesearch_helper::getCategories($uid, $tablename); - if (count($categories['uid_list'])) { - foreach ($categories['uid_list'] as $category_uid) { - tx_kesearch_helper::makeTags($tags, array('syscat' . $category_uid)); - } - } - } + /** + * finds the system categories for $uid in $tablename, creates + * tags like "syscat123" ("syscat" + category uid). + * + * @param string $tags + * @param integer $uid + * @param string $tablename + * @author Christian Bülter + * @since 24.09.15 + */ + public static function makeSystemCategoryTags(&$tags, $uid, $tablename) + { + $categories = tx_kesearch_helper::getCategories($uid, $tablename); + if (count($categories['uid_list'])) { + foreach ($categories['uid_list'] as $category_uid) { + tx_kesearch_helper::makeTags($tags, array('syscat' . $category_uid)); + } + } + } - /** - * renders a link to a search result - * - * @param array $resultRow - * @param string $targetDefault - * @param string $targetFiles - * @author Christian Bülter - * @since 31.10.14 - * @return array - */ - public static function getResultLinkConfiguration($resultRow, $targetDefault='', $targetFiles='') { - $linkconf = array(); + /** + * renders a link to a search result + * + * @param array $resultRow + * @param string $targetDefault + * @param string $targetFiles + * @author Christian Bülter + * @since 31.10.14 + * @return array + */ + public static function getResultLinkConfiguration($resultRow, $targetDefault = '', $targetFiles = '') + { + $linkconf = array(); - list($type) = explode(':', $resultRow['type']); + list($type) = explode(':', $resultRow['type']); - switch($type) { + switch ($type) { + case 'file': + // render a link for files + // if we use FAL, we can use the API + if ($resultRow['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($resultRow['orig_uid']))) { + $linkconf['parameter'] = $fileObject->getPublicUrl(); + } else { + $linkconf['parameter'] = $resultRow['directory'] . rawurlencode($resultRow['title']); + } + $linkconf['fileTarget'] = $targetFiles; + break; - case 'file': - // render a link for files - // if we use FAL, we can use the API - if ($resultRow['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($resultRow['orig_uid']))) { - $linkconf['parameter'] = $fileObject->getPublicUrl(); - } else { - $linkconf['parameter'] = $resultRow['directory'] . rawurlencode($resultRow['title']); - } - $linkconf['fileTarget'] = $targetFiles; - break; + case 'external': + // render a link for external results (provided by eg. ke_search_premium or tt_news) + $linkconf['parameter'] = $resultRow['params']; + $linkconf['useCacheHash'] = false; + $linkconf['additionalParams'] = ''; + $extConfPremium = tx_kesearch_helper::getExtConfPremium(); + $linkconf['extTarget'] = $extConfPremium['apiExternalResultTarget'] ? + $extConfPremium['apiExternalResultTarget'] : '_blank'; + break; - case 'external': - // render a link for external results (provided by eg. ke_search_premium or tt_news) - $linkconf['parameter'] = $resultRow['params']; - $linkconf['useCacheHash'] = false; - $linkconf['additionalParams'] = ''; - $extConfPremium = tx_kesearch_helper::getExtConfPremium(); - $linkconf['extTarget'] = $extConfPremium['apiExternalResultTarget'] ? $extConfPremium['apiExternalResultTarget'] : '_blank'; - break; + default: + // render a link for page targets + // if params are filled, add them to the link generation process + if (!empty($resultRow['params'])) { + $linkconf['additionalParams'] = $resultRow['params']; + } + $linkconf['parameter'] = $resultRow['targetpid']; + $linkconf['useCacheHash'] = true; + $linkconf['target'] = $targetDefault; + break; + } - default: - // render a link for page targets - // if params are filled, add them to the link generation process - if (!empty($resultRow['params'])) { - $linkconf['additionalParams'] = $resultRow['params']; - } - $linkconf['parameter'] = $resultRow['targetpid']; - $linkconf['useCacheHash'] = true; - $linkconf['target'] = $targetDefault; - break; - } + return $linkconf; + } - return $linkconf; - } + /** + * @param integer $uid + * @return File|NULL + */ + public static function getFile($uid) + { + try { + $fileObject = ResourceFactory::getInstance()->getFileObject($uid); + } catch (\TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException $e) { + $fileObject = null; + } - /** - * - * @param integer $uid - * @return File|NULL - */ - public static function getFile($uid) { - - try { - $fileObject = ResourceFactory::getInstance()->getFileObject($uid); - } catch (\TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException $e) { - $fileObject = NULL; - } - - return $fileObject; - - } + return $fileObject; + } } + diff --git a/Classes/lib/class.tx_kesearch_lib_items.php b/Classes/lib/class.tx_kesearch_lib_items.php index e638b81b..55a75575 100644 --- a/Classes/lib/class.tx_kesearch_lib_items.php +++ b/Classes/lib/class.tx_kesearch_lib_items.php @@ -1,43 +1,39 @@ registerIndexerConfiguration($params, $pObj); - } - } - } -} \ No newline at end of file +class tx_kesearch_lib_items +{ + public function fillIndexerConfig(&$params, $pObj) + { + // hook for custom registration of further indexerConfigurations + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['registerIndexerConfiguration'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['registerIndexerConfiguration'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->registerIndexerConfiguration($params, $pObj); + } + } + } +} diff --git a/Classes/lib/class.tx_kesearch_lib_searchphrase.php b/Classes/lib/class.tx_kesearch_lib_searchphrase.php index f5e434eb..7c2854ea 100644 --- a/Classes/lib/class.tx_kesearch_lib_searchphrase.php +++ b/Classes/lib/class.tx_kesearch_lib_searchphrase.php @@ -1,232 +1,240 @@ pObj = $pObj; - } - - - /** - * build search phrase - * - * @return array Array containing different elements which helps to build the search query - */ - public function buildSearchPhrase() { - $cleanSearchStringParts = array(); - $tagsAgainst = $this->buildTagsAgainst(); - $searchString = trim($this->pObj->piVars['sword']); - $searchString = $this->checkAgainstDefaultValue($searchString); - $searchStringParts = $this->explodeSearchPhrase($searchString); - foreach($searchStringParts as $key => $part) { - $part = trim($part, '\~+-*|"'); - if(!empty($part)) { - $cleanSearchStringParts[$key] = $part; - } - } - - $searchArray = array( - 'sword' => implode(' ', $cleanSearchStringParts), // f.e. hello karl-heinz +mueller - 'swords' => $cleanSearchStringParts, // f.e. Array: hello|karl|heinz|mueller - 'wordsAgainst' => implode(' ', $searchStringParts), // f.e. +hello* +karl* +heinz* +mueller* - 'tagsAgainst' => $tagsAgainst, // f.e. Array: +#category_213# +#color_123# +#city_42# - 'scoreAgainst' => implode(' ', $cleanSearchStringParts) // f.e. hello karl heinz mueller - ); - - return $searchArray; - } - - - /** - * checks if the entered searchstring is the default value - * - * @param string $searchString - * @return string Returns the searchstring or an empty string - */ - public function checkAgainstDefaultValue($searchString) { +class tx_kesearch_lib_searchphrase +{ + + /** + * @var tx_kesearch_lib + */ + protected $pObj; + + /** + * initializes this object + * @param tx_kesearch_lib $pObj + */ + public function initialize(tx_kesearch_lib $pObj) + { + $this->pObj = $pObj; + } + + /** + * build search phrase + * @return array Array containing different elements which helps to build the search query + */ + public function buildSearchPhrase() + { + $cleanSearchStringParts = array(); + $tagsAgainst = $this->buildTagsAgainst(); + $searchString = trim($this->pObj->piVars['sword']); + $searchString = $this->checkAgainstDefaultValue($searchString); + $searchStringParts = $this->explodeSearchPhrase($searchString); + foreach ($searchStringParts as $key => $part) { + $part = trim($part, '\~+-*|"'); + if (!empty($part)) { + $cleanSearchStringParts[$key] = $part; + } + } + + $searchArray = array( + 'sword' => implode(' ', $cleanSearchStringParts), // f.e. hello karl-heinz +mueller + 'swords' => $cleanSearchStringParts, // f.e. Array: hello|karl|heinz|mueller + 'wordsAgainst' => implode(' ', $searchStringParts), // f.e. +hello* +karl* +heinz* +mueller* + 'tagsAgainst' => $tagsAgainst, // f.e. Array: +#category_213# +#color_123# +#city_42# + 'scoreAgainst' => implode(' ', $cleanSearchStringParts) // f.e. hello karl heinz mueller + ); + + return $searchArray; + } + + /** + * checks if the entered searchstring is the default value + * @param string $searchString + * @return string Returns the searchstring or an empty string + */ + public function checkAgainstDefaultValue($searchString) + { $searchStringToLower = strtolower(trim($searchString)); $defaultValueToLower = strtolower($this->pObj->pi_getLL('searchbox_default_value')); - if ($searchStringToLower === $defaultValueToLower) $searchString = ''; + if ($searchStringToLower === $defaultValueToLower) { + $searchString = ''; + } return $searchString; } - /** - * explode search string and remove too short words - * - * @param type $searchString - * @return type - */ - public function explodeSearchPhrase($searchString) { - preg_match_all('/(\+|\-|\~|<|>)?\".*?"|[^ ]+/', $searchString, $matches); - list($searchParts) = $matches; - if(is_array($searchParts) && count($searchParts)) { - foreach($searchParts as $key => $word) { - // check for boolean seperator - if($word === '|') continue; - - // maybe we should check against the MySQL stoppword list: - // Link: http://dev.mysql.com/doc/refman/5.0/en/fulltext-stopwords.html - - // don't check length if it is a phrase - if(preg_match('/^(\+|\-|\~|<|>)?\"/', $word)) continue; - - // prepare word for next check - $word = trim($word, '+-~<>'); - - // check for word length - $csconv = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter'); - $searchWordLength = $csconv->utf8_strlen($word); - if($searchWordLength < $this->pObj->extConf['searchWordLength']) { - $this->pObj->hasTooShortWords = true; - $this->showShortMessage = true; - unset($searchParts[$key]); - } - } - foreach($searchParts as $key => $word) { - if($word != '|') { - - // enable part searching by default. But be careful: Enabling this slows down the search engine - if(!isset($this->pObj->extConf['enablePartSearch']) || $this->pObj->extConf['enablePartSearch']) { - if($this->pObj->extConfPremium['enableInWordSearch']) { - $searchParts[$key] = '*' . trim($searchParts[$key], '*') . '*'; - } else { - $searchParts[$key] = rtrim($searchParts[$key], '*') . '*'; - } - } - - // add + explicit to all search words to make the searchresults equal to sphinx search results - if($this->pObj->extConf['enableExplicitAnd']) { - $searchParts[$key] = '+' . ltrim($searchParts[$key], '+'); - } - } - - // make the words save for the database - $searchParts[$key] = $GLOBALS['TYPO3_DB']->quoteStr($searchParts[$key], 'tx_kesearch_index'); - } - return array_values($searchParts); - } return array(); - } - - - /** - * build tags against - * - * @return array - */ - public function buildTagsAgainst() { - $tagsAgainst = array(); - $this->buildPreselectedTagsAgainst($tagsAgainst); - $this->buildPiVarsTagsAgainst($tagsAgainst); - - return $tagsAgainst; - } - - - /** - * add preselected filter options (preselected in the backend flexform) - * - * @param array $tagsAgainst - */ - public function buildPreselectedTagsAgainst(array &$tagsAgainst) { - $tagChar = $this->pObj->extConf['prePostTagChar']; - foreach($this->pObj->preselectedFilter as $key => $filterTags) { - // add it only, if no other filter options of this filter has been - // selected in the frontend - if (!isset($this->pObj->piVars['filter'][$key]) && !is_array($this->pObj->piVars['filter'][$key])) { - // Quote the tags for use in database query - foreach ($filterTags as $k => $v) { - $filterTags[$k] = $GLOBALS['TYPO3_DB']->quoteStr($v, 'tx_kesearch_index'); - } - // if we are in checkbox mode - if(count($this->pObj->preselectedFilter[$key]) >= 2) { - $tagsAgainst[$key] .= ' "' . $tagChar . implode($tagChar . '" "' . $tagChar, $filterTags) . $tagChar . '"'; - // if we are in select or list mode - } elseif(count($this->pObj->preselectedFilter[$key]) == 1) { - $tagsAgainst[$key] .= ' +"' . $tagChar . array_shift($filterTags) . $tagChar . '"'; - } - } - } - } - - /** - * add filter options (preselected by piVars) - * - * @param array $tagsAgainst - */ - public function buildPiVarsTagsAgainst(array &$tagsAgainst) { - // add filter options selected in the frontend - $tagChar = $this->pObj->extConf['prePostTagChar']; - if(is_array($this->pObj->piVars['filter'])) { - foreach($this->pObj->piVars['filter'] as $key => $tag) { - if(is_array($this->pObj->piVars['filter'][$key])) { - foreach($this->pObj->piVars['filter'][$key] as $subkey => $subtag) { - // Don't add the tag if it is already inserted by preselected filters - if(!empty($subtag) && strstr($tagsAgainst[$key], $subtag) === false) { - // Don't add a "+", because we are here in checkbox mode. It's a OR. - $tagsAgainst[$key] .= ' "' . $tagChar . $GLOBALS['TYPO3_DB']->quoteStr($subtag, 'tx_kesearch_index') . $tagChar . '"'; - } - } - } else { - // Don't add the tag if it is already inserted by preselected filters - if(!empty($tag) && strstr($tagsAgainst[$key], $subtag) === false) { - $tagsAgainst[$key] .= ' +"' . $tagChar . $GLOBALS['TYPO3_DB']->quoteStr($tag, 'tx_kesearch_index') . $tagChar . '"'; - } - } - } - } - - // hook for modifiying the tags to filter for - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTagsAgainst'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTagsAgainst'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $_procObj->modifyTagsAgainst($tagsAgainst, $this); - } - } - } -} \ No newline at end of file + /** + * explode search string and remove too short words + * @param type $searchString + * @return type + */ + public function explodeSearchPhrase($searchString) + { + preg_match_all('/(\+|\-|\~|<|>)?\".*?"|[^ ]+/', $searchString, $matches); + list($searchParts) = $matches; + if (is_array($searchParts) && count($searchParts)) { + foreach ($searchParts as $key => $word) { + // check for boolean seperator + if ($word === '|') { + continue; + } + + // maybe we should check against the MySQL stoppword list: + // Link: http://dev.mysql.com/doc/refman/5.0/en/fulltext-stopwords.html + + // don't check length if it is a phrase + if (preg_match('/^(\+|\-|\~|<|>)?\"/', $word)) { + continue; + } + + // prepare word for next check + $word = trim($word, '+-~<>'); + + // check for word length + $csconv = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter'); + $searchWordLength = $csconv->utf8_strlen($word); + if ($searchWordLength < $this->pObj->extConf['searchWordLength']) { + $this->pObj->hasTooShortWords = true; + $this->showShortMessage = true; + unset($searchParts[$key]); + } + } + foreach ($searchParts as $key => $word) { + if ($word != '|') { + // enable part searching by default. But be careful: Enabling this slows down the search engine + if (!isset($this->pObj->extConf['enablePartSearch']) || $this->pObj->extConf['enablePartSearch']) { + if ($this->pObj->extConfPremium['enableInWordSearch']) { + $searchParts[$key] = '*' . trim($searchParts[$key], '*') . '*'; + } else { + $searchParts[$key] = rtrim($searchParts[$key], '*') . '*'; + } + } + + // add + explicit to all search words to make the searchresults equal to sphinx search results + if ($this->pObj->extConf['enableExplicitAnd']) { + $searchParts[$key] = '+' . ltrim($searchParts[$key], '+'); + } + } + + // make the words save for the database + $searchParts[$key] = $GLOBALS['TYPO3_DB']->quoteStr($searchParts[$key], 'tx_kesearch_index'); + } + return array_values($searchParts); + } + return array(); + } + + /** + * build tags against + * @return array + */ + public function buildTagsAgainst() + { + $tagsAgainst = array(); + $this->buildPreselectedTagsAgainst($tagsAgainst); + $this->buildPiVarsTagsAgainst($tagsAgainst); + + return $tagsAgainst; + } + + /** + * add preselected filter options (preselected in the backend flexform) + * @param array $tagsAgainst + */ + public function buildPreselectedTagsAgainst(array &$tagsAgainst) + { + $tagChar = $this->pObj->extConf['prePostTagChar']; + foreach ($this->pObj->preselectedFilter as $key => $filterTags) { + // add it only, if no other filter options of this filter has been + // selected in the frontend + if (!isset($this->pObj->piVars['filter'][$key]) && !is_array($this->pObj->piVars['filter'][$key])) { + // Quote the tags for use in database query + foreach ($filterTags as $k => $v) { + $filterTags[$k] = $GLOBALS['TYPO3_DB']->quoteStr($v, 'tx_kesearch_index'); + } + // if we are in checkbox mode + if (count($this->pObj->preselectedFilter[$key]) >= 2) { + $tagsAgainst[$key] .= ' "' + . $tagChar + . implode($tagChar . '" "' . $tagChar, $filterTags) + . $tagChar + . '"'; + // if we are in select or list mode + } elseif (count($this->pObj->preselectedFilter[$key]) == 1) { + $tagsAgainst[$key] .= ' +"' . $tagChar . array_shift($filterTags) . $tagChar . '"'; + } + } + } + } + + /** + * add filter options (preselected by piVars) + * + * @param array $tagsAgainst + */ + public function buildPiVarsTagsAgainst(array &$tagsAgainst) + { + // add filter options selected in the frontend + $tagChar = $this->pObj->extConf['prePostTagChar']; + if (is_array($this->pObj->piVars['filter'])) { + foreach ($this->pObj->piVars['filter'] as $key => $tag) { + if (is_array($this->pObj->piVars['filter'][$key])) { + foreach ($this->pObj->piVars['filter'][$key] as $subkey => $subtag) { + // Don't add the tag if it is already inserted by preselected filters + if (!empty($subtag) && strstr($tagsAgainst[$key], $subtag) === false) { + // Don't add a "+", because we are here in checkbox mode. It's a OR. + $tagsAgainst[$key] .= ' "' + . $tagChar + . $GLOBALS['TYPO3_DB']->quoteStr($subtag, 'tx_kesearch_index') + . $tagChar + . '"'; + } + } + } else { + // Don't add the tag if it is already inserted by preselected filters + if (!empty($tag) && strstr($tagsAgainst[$key], $subtag) === false) { + $tagsAgainst[$key] .= ' +"' + . $tagChar + . $GLOBALS['TYPO3_DB']->quoteStr($tag, 'tx_kesearch_index') + . $tagChar + . '"'; + } + } + } + } + + // hook for modifiying the tags to filter for + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTagsAgainst'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTagsAgainst'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->modifyTagsAgainst($tagsAgainst, $this); + } + } + } +} diff --git a/Classes/lib/class.tx_kesearch_lib_searchresult.php b/Classes/lib/class.tx_kesearch_lib_searchresult.php index 6a3a3eea..b43a58d4 100644 --- a/Classes/lib/class.tx_kesearch_lib_searchresult.php +++ b/Classes/lib/class.tx_kesearch_lib_searchresult.php @@ -1,294 +1,301 @@ init($pObj); - } - - - /** - * Initializes this object - * - * @param tx_kesearch_lib $pObj - * @return void - */ - public function init(tx_kesearch_lib $pObj) { - $this->pObj = $pObj; - $this->cObj = $this->pObj->cObj; - $this->conf = $this->pObj->conf; - } - - - /** - * set row array with current result element - * - * @param array $row - * @return void - */ - public function setRow(array $row) { - $this->row = $row; - } - - - /** - * get title for result row - * - * @return string The linked result title - */ - public function getTitle() { - // configure the link - $linkconf = $this->getResultLinkConfiguration(); - - list($type) = explode(':', $this->row['type']); - switch($type) { - case 'file': - // if we use FAL, see if we have a title in the metadata - if ($this->row['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($this->row['orig_uid']))) { - $metadata = $fileObject->_getMetaData(); - $linktext = ($metadata['title'] ? $metadata['title'] : $this->row['title']); - } else { - $linktext = $this->row['title']; - } - break; - default: - $linktext = $this->row['title']; - break; - } - - // clean title - $linktext = strip_tags($linktext); - $linktext = $this->pObj->div->removeXSS($linktext); - - // highlight hits in result title? - if($this->conf['highlightSword'] && count($this->pObj->swords)) { - $linktext = $this->highlightArrayOfWordsInContent($this->pObj->swords, $linktext); - } - return $this->cObj->typoLink($linktext, $linkconf); - } - - - /** - * get result url (not) linked - * - * @return string The results URL - */ - public function getResultUrl($linked = FALSE) { - $linkText = $this->cObj->typoLink_URL($this->getResultLinkConfiguration()); - $linkText = htmlspecialchars($linkText); - $resultUrl = $this->cObj->typoLink($linkText, $this->getResultLinkConfiguration()); - if($linked) { - return $this->cObj->typoLink($linkText, $this->getResultLinkConfiguration()); - } return $linkText; - } - - - /** - * get result link configuration - * It can devide between the result types (file, page, content) - * - * @return array configuration for typolink - */ - public function getResultLinkConfiguration() { - return tx_kesearch_helper::getResultLinkConfiguration($this->row, $this->conf['resultLinkTarget'], $this->conf['resultLinkTargetFiles']); - } - - - /** - * get teaser for result list - * - * @return string The teaser - */ - public function getTeaser() { - $content = $this->getContentForTeaser(); - return $this->buildTeaserContent($content); - } - - - /** - * get content for teaser - * This can be the abstract or content col - * - * @return string The content - */ - public function getContentForTeaser() { - $content = $this->row['content']; - if (!empty($this->row['abstract'])) { - $content = nl2br($this->row['abstract']); - if ($this->conf['previewMode'] == 'hit') { - if (!$this->isArrayOfWordsInString($this->pObj->swords, $this->row['abstract'])) { - $content = $this->row['content']; - } - } - } - return $content; - } - - - /** - * check if an array with words was found in given content - * - * @param array $wordArray A single dimmed Array containing words to search for. F.E. array('hello', 'georg', 'company') - * @param string $content The string to search in - * @param boolean $checkAll If this is checked, then all words have to be found in string. If false: The method returns true directly, if one of the words was found - * @return boolean Returns true if the word(s) are found - */ - public function isArrayOfWordsInString(array $wordArray, $content, $checkAll = FALSE) { - $found = FALSE; - foreach($wordArray as $word) { - if(stripos($content, $word) === FALSE) { - $found = FALSE; - if($checkAll === TRUE) return FALSE; - } else { - $found = TRUE; - if($checkAll === FALSE) return TRUE; - } - } - return $found; - } - - - /** - * Find and highlight the searchwords - * - * @param array $wordArray - * @param string $content - * @return string The content with highlighted searchwords - */ - public function highlightArrayOfWordsInContent($wordArray, $content) { - if(is_array($wordArray) && count($wordArray)) { - $highlightedWord = (!empty($this->conf['highlightedWord_stdWrap.']))? - $this->cObj->stdWrap('\0', $this->conf['highlightedWord_stdWrap.']) : - '\0'; - - foreach($wordArray as $word) { - $word = str_replace('/', '\/', $word); - $word = htmlspecialchars($word); - $content = preg_replace('/(' . $word . ')/iu', $highlightedWord, $content); - } - } - return $content; - } - - - /** - * Build Teasercontent - * - * @param string $content The whole resultcontent - * @return string The cutted recultcontent - */ - public function buildTeaserContent($content) { - if(is_array($this->pObj->swords) && count($this->pObj->swords)) { - $amountOfSearchWords = count($this->pObj->swords); - $content = strip_tags($content); - // with each new searchword and all the croppings here the teaser for each word will become too small/short - // I decided to add 20 additional letters for each searchword. It looks much better and is more readable - $charsForEachSearchWord = ceil($this->conf['resultChars'] / $amountOfSearchWords) + 20; - $charsBeforeAfterSearchWord = ceil($charsForEachSearchWord / 2); - $aSearchWordWasFound = FALSE; - $isSearchWordAtTheBeginning = FALSE; - foreach($this->pObj->swords as $word) { - $word = ' ' . $word; // our searchengine searches for wordbeginnings - $pos = stripos($content, $word); - if($pos === FALSE) { - // if the word was not found it could be within brakets => (searchWord) - // so give it a second try - $pos = stripos($content, trim($word)); - if($pos === FALSE) { - continue; - } - } - $aSearchWordWasFound = TRUE; - - // if searchword is the first word - if($pos === 0) { - $isSearchWordAtTheBeginning = TRUE; - } - - // find search starting point - $startPos = $pos - $charsBeforeAfterSearchWord; - if($startPos < 0) $startPos = 0; - - // crop some words behind searchword - $partWithSearchWord = substr($content, $startPos); - $temp = $this->cObj->crop($partWithSearchWord, $charsForEachSearchWord . '|...|1'); - - // crop some words before searchword - // after last cropping our text is too short now. So we have to find a new cutting position - ($startPos > 10)? $length = strlen($temp) - 10: $length = strlen($temp); - $teaserArray[] = $this->cObj->crop($temp, '-' . $length . '||1'); - } - - // When the searchword was found in title but not in content the teaser is empty - // in that case we have to get the first x letters without containing any searchword - if($aSearchWordWasFound === FALSE) { - $teaser = $this->cObj->crop($content, $this->conf['resultChars'] . '||1'); - } elseif($isSearchWordAtTheBeginning === TRUE) { - $teaser = implode(' ', $teaserArray); - } else { - $teaser = '...' . implode(' ', $teaserArray); - } - - // highlight hits? - if ($this->conf['highlightSword']) { - $teaser = $this->highlightArrayOfWordsInContent($this->pObj->swords, $teaser); - } - return $teaser; - } else return $this->cObj->crop($content, $this->conf['resultChars'] . '|...|1'); - } +class tx_kesearch_lib_searchresult +{ + + protected $conf = array(); + protected $row = array(); + + /** + * @var tx_kesearch_lib + */ + protected $pObj; + + /** + * @var tslib_cObj + */ + protected $cObj; + + /** + * @var tx_kesearch_lib_div + */ + protected $div; + + + /** + * The constructor of this class + * @param tx_kesearch_lib $pObj + */ + public function __construct(tx_kesearch_lib $pObj) + { + // initializes this object + $this->init($pObj); + } + + + /** + * Initializes this object + * @param tx_kesearch_lib $pObj + * @return void + */ + public function init(tx_kesearch_lib $pObj) + { + $this->pObj = $pObj; + $this->cObj = $this->pObj->cObj; + $this->conf = $this->pObj->conf; + } + + + /** + * set row array with current result element + * @param array $row + * @return void + */ + public function setRow(array $row) + { + $this->row = $row; + } + + + /** + * get title for result row + * @return string The linked result title + */ + public function getTitle() + { + // configure the link + $linkconf = $this->getResultLinkConfiguration(); + + list($type) = explode(':', $this->row['type']); + switch ($type) { + case 'file': + // if we use FAL, see if we have a title in the metadata + if ($this->row['orig_uid'] && ($fileObject = tx_kesearch_helper::getFile($this->row['orig_uid']))) { + $metadata = $fileObject->_getMetaData(); + $linktext = ($metadata['title'] ? $metadata['title'] : $this->row['title']); + } else { + $linktext = $this->row['title']; + } + break; + default: + $linktext = $this->row['title']; + break; + } + + // clean title + $linktext = strip_tags($linktext); + $linktext = $this->pObj->div->removeXSS($linktext); + + // highlight hits in result title? + if ($this->conf['highlightSword'] && count($this->pObj->swords)) { + $linktext = $this->highlightArrayOfWordsInContent($this->pObj->swords, $linktext); + } + return $this->cObj->typoLink($linktext, $linkconf); + } + + /** + * get result url (not) linked + * @return string The results URL + */ + public function getResultUrl($linked = false) + { + $linkText = $this->cObj->typoLink_URL($this->getResultLinkConfiguration()); + $linkText = htmlspecialchars($linkText); + if ($linked) { + return $this->cObj->typoLink($linkText, $this->getResultLinkConfiguration()); + } + return $linkText; + } + + /** + * get result link configuration + * It can devide between the result types (file, page, content) + * + * @return array configuration for typolink + */ + public function getResultLinkConfiguration() + { + return tx_kesearch_helper::getResultLinkConfiguration( + $this->row, + $this->conf['resultLinkTarget'], + $this->conf['resultLinkTargetFiles'] + ); + } + + /** + * get teaser for result list + * + * @return string The teaser + */ + public function getTeaser() + { + $content = $this->getContentForTeaser(); + return $this->buildTeaserContent($content); + } + + /** + * get content for teaser + * This can be the abstract or content col + * + * @return string The content + */ + public function getContentForTeaser() + { + $content = $this->row['content']; + if (!empty($this->row['abstract'])) { + $content = nl2br($this->row['abstract']); + if ($this->conf['previewMode'] == 'hit') { + if (!$this->isArrayOfWordsInString($this->pObj->swords, $this->row['abstract'])) { + $content = $this->row['content']; + } + } + } + return $content; + } + + /** + * check if an array with words was found in given content + * @param array $wordArray A single dimmed Array containing words + * to search for. F.E. array('hello', 'georg', 'company') + * @param string $content The string to search in + * @param boolean $checkAll If this is checked, then all words have to be found in string. + * If false: The method returns true directly, if one of the words was found + * @return boolean Returns true if the word(s) are found + */ + public function isArrayOfWordsInString(array $wordArray, $content, $checkAll = false) + { + $found = false; + foreach ($wordArray as $word) { + if (stripos($content, $word) === false) { + $found = false; + if ($checkAll === true) { + return false; + } + } else { + $found = true; + if ($checkAll === false) { + return true; + } + } + } + return $found; + } + + /** + * Find and highlight the searchwords + * + * @param array $wordArray + * @param string $content + * @return string The content with highlighted searchwords + */ + public function highlightArrayOfWordsInContent($wordArray, $content) + { + if (is_array($wordArray) && count($wordArray)) { + $highlightedWord = (!empty($this->conf['highlightedWord_stdWrap.'])) ? + $this->cObj->stdWrap('\0', $this->conf['highlightedWord_stdWrap.']) : + '\0'; + + foreach ($wordArray as $word) { + $word = str_replace('/', '\/', $word); + $word = htmlspecialchars($word); + $content = preg_replace('/(' . $word . ')/iu', $highlightedWord, $content); + } + } + return $content; + } + + /** + * Build Teasercontent + * + * @param string $content The whole resultcontent + * @return string The cutted recultcontent + */ + public function buildTeaserContent($content) + { + if (is_array($this->pObj->swords) && count($this->pObj->swords)) { + $amountOfSearchWords = count($this->pObj->swords); + $content = strip_tags($content); + // with each new searchword and all the croppings here the teaser for each word will become too small/short + // I decided to add 20 additional letters for each searchword. It looks much better and is more readable + $charsForEachSearchWord = ceil($this->conf['resultChars'] / $amountOfSearchWords) + 20; + $charsBeforeAfterSearchWord = ceil($charsForEachSearchWord / 2); + $aSearchWordWasFound = false; + $isSearchWordAtTheBeginning = false; + foreach ($this->pObj->swords as $word) { + $word = ' ' . $word; // our searchengine searches for wordbeginnings + $pos = stripos($content, $word); + if ($pos === false) { + // if the word was not found it could be within brakets => (searchWord) + // so give it a second try + $pos = stripos($content, trim($word)); + if ($pos === false) { + continue; + } + } + $aSearchWordWasFound = true; + + // if searchword is the first word + if ($pos === 0) { + $isSearchWordAtTheBeginning = true; + } + + // find search starting point + $startPos = $pos - $charsBeforeAfterSearchWord; + if ($startPos < 0) { + $startPos = 0; + } + + // crop some words behind searchword + $partWithSearchWord = substr($content, $startPos); + $temp = $this->cObj->crop($partWithSearchWord, $charsForEachSearchWord . '|...|1'); + + // crop some words before searchword + // after last cropping our text is too short now. So we have to find a new cutting position + ($startPos > 10) ? $length = strlen($temp) - 10 : $length = strlen($temp); + $teaserArray[] = $this->cObj->crop($temp, '-' . $length . '||1'); + } + + // When the searchword was found in title but not in content the teaser is empty + // in that case we have to get the first x letters without containing any searchword + if ($aSearchWordWasFound === false) { + $teaser = $this->cObj->crop($content, $this->conf['resultChars'] . '||1'); + } elseif ($isSearchWordAtTheBeginning === true) { + $teaser = implode(' ', $teaserArray); + } else { + $teaser = '...' . implode(' ', $teaserArray); + } + + // highlight hits? + if ($this->conf['highlightSword']) { + $teaser = $this->highlightArrayOfWordsInContent($this->pObj->swords, $teaser); + } + return $teaser; + } else { + return $this->cObj->crop($content, $this->conf['resultChars'] . '|...|1'); + } + } } diff --git a/Classes/lib/class.tx_kesearch_lib_sorting.php b/Classes/lib/class.tx_kesearch_lib_sorting.php index f5debe76..4415abba 100644 --- a/Classes/lib/class.tx_kesearch_lib_sorting.php +++ b/Classes/lib/class.tx_kesearch_lib_sorting.php @@ -1,242 +1,256 @@ init($pObj); - } - - - /** - * Initializes this object - * - * @param tx_kesearch_lib $pObj - * @return void - */ - public function init(tx_kesearch_lib $pObj) { - $this->pObj = $pObj; - $this->db = $this->pObj->db; - $this->cObj = $this->pObj->cObj; - $this->conf = $this->pObj->conf; - - // get subparts - $this->subpartArray['###ORDERNAVIGATION###'] = $this->cObj->getSubpart($this->pObj->templateCode, '###ORDERNAVIGATION###'); - $this->subpartArray['###SORT_LINK###'] = $this->cObj->getSubpart($this->subpartArray['###ORDERNAVIGATION###'], '###SORT_LINK###'); - - // get sorting values (sortdate, title, what ever...) - $this->sortBy = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['sortByVisitor'], true); - } - - - /** - * The main entry point of this class - * It will return the complete sorting HTML - * - * @return string HTML - */ - public function renderSorting(&$fluidTemplateVariables) { - // show sorting: - // if show Sorting is activated in FlexForm - // if a value to sortBy is set in FlexForm (title, relevance, sortdate, what ever...) - // if there are any entries in current search results - if($this->conf['showSortInFrontend'] && !empty($this->conf['sortByVisitor']) && $this->pObj->numberOfResults) { - // loop all allowed orderings - foreach($this->sortBy as $field) { - // we can't sort by score if there is no sword given - if($this->pObj->sword != '' || $field != 'score') { - $sortByDir = $this->getDefaultSortingDirection($field); - $dbOrdering = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(' ', $this->db->getOrdering()); - - /* if ordering direction is the same change it - * - * Explanation: - * No ordering is active. Default Ordering by db is "sortdate desc". - * Default ordering by current field is also "sortdate desc". - * So...if you click the link for sortdate it will sort the results by "sortdate desc" again - * To prevent this we change the default ordering here - */ - if($field == $dbOrdering[0] && $sortByDir == $dbOrdering[1]) { - $sortByDir = $this->changeOrdering($sortByDir); - } - - $markerArray['###FIELDNAME###'] = $field; - $markerArray['###URL###'] = $url = $this->generateSortingLink($field, $sortByDir); - $markerArray['###CLASS###'] = $classname = $this->getClassNameForUpDownArrow($field, $dbOrdering); - - $links .= $this->cObj->substituteMarkerArray($this->subpartArray['###SORT_LINK###'], $markerArray); - - $fluidTemplateVariables['sortingLinks'][] = array( - 'field' => $field, - 'url' => $url, - 'class' => $classname - ); - } - } - - $content = $this->cObj->substituteSubpart($this->subpartArray['###ORDERNAVIGATION###'], '###SORT_LINK###', $links); - $content = $this->cObj->substituteMarker($content, '###LABEL_SORT###', $this->pObj->pi_getLL('label_sort')); - - return $content; - } else { - return ''; - } - } - - - /** - * get default sorting direction - * f.e. default sorting for sortdate should be DESC. The most current records at first - * f.e. default sorting for relevance should be DESC. The best relevance at first - * f.e. default sorting for title should be ASC. Alphabetic order begins with A. - * - * @param string The field name to sort by - * @return string The default sorting (asc/desc) for given field - */ - public function getDefaultSortingDirection($field) { - if(!empty($field) && is_string($field)) { - switch($field) { - case 'sortdate': - case 'score': - $orderBy = 'desc'; - break; - case 'title': - default: - $orderBy = 'asc'; - break; - } - return $orderBy; - } else return 'asc'; - } - - - /** - * change ordering - * f.e. asc to desc and desc to asc - * - * @param string $direction asc or desc - * @return string desc or asc. If you call this function with a not allowed string, exactly this string will be returned. Short: The function do nothing - */ - public function changeOrdering($direction) { - $allowedDirections = array('asc', 'desc'); - $direction = strtolower($direction); - $isInArray = TYPO3\CMS\Core\Utility\GeneralUtility::inArray($allowedDirections, $direction); - if(!empty($direction) && $isInArray) { - if($direction == 'asc') { - $direction = 'desc'; - } else $direction = 'asc'; - } return $direction; - } - - - /** - * get a class name for up and down arrows of sorting links - * - * @param string $field current field to sort by - * @param array $dbOrdering An array containing the field and ordering of current DB Ordering - * @return string The class name - */ - public function getClassNameForUpDownArrow($field, $dbOrdering) { - $className = ''; - if(is_array($dbOrdering) && count($dbOrdering)) { - if($field == $dbOrdering[0]) { - if($dbOrdering[1] == 'asc') { - $className = 'up'; - } else $className = 'down'; - } - } - return $className; - } - - - /** - * generate the link for the given sorting value - * - * @param string $field - * @param string $sortByDir - * @return string The complete link as A-Tag - */ - public function generateSortingLink($field, $sortByDir) { - $params = array(); - $params['sortByField'] = $field; - $params['sortByDir'] = $sortByDir; - - foreach($params as $key => $value) { - $params[$key] = $this->cObj->wrap($value, $this->pObj->prefixId . '[' . $key . ']=|'); - } - - $conf = array(); - $conf['parameter'] = $GLOBALS['TSFE']->id; - $conf['addQueryString'] = '1'; - $conf['addQueryString.']['exclude'] = 'id,tx_kesearch_pi1[multi],cHash'; - $conf['additionalParams'] = '&' . implode('&', $params); - - return $this->cObj->typoLink( - $this->pObj->pi_getLL('orderlink_' . $field, $field), - $conf - ); - } +class tx_kesearch_lib_sorting +{ + + public $conf = array(); + public $subpartArray = array(); + public $sortBy = ''; + + /** + * @var tx_kesearch_lib + */ + public $pObj; + + /** + * @var tx_kesearch_db + */ + public $db; + + /** + * @var tslib_cObj + */ + public $cObj; + + /** + * @var tx_kesearch_lib_div + */ + public $div; + + /** + * The constructor of this class + * @param tx_kesearch_lib $pObj + */ + public function __construct(tx_kesearch_lib $pObj) + { + // initializes this object + $this->init($pObj); + } + + + /** + * Initializes this object + * @param tx_kesearch_lib $pObj + * @return void + */ + public function init(tx_kesearch_lib $pObj) + { + $this->pObj = $pObj; + $this->db = $this->pObj->db; + $this->cObj = $this->pObj->cObj; + $this->conf = $this->pObj->conf; + + // get subparts + $this->subpartArray['###ORDERNAVIGATION###'] = $this->cObj->getSubpart( + $this->pObj->templateCode, + '###ORDERNAVIGATION###' + ); + $this->subpartArray['###SORT_LINK###'] = $this->cObj->getSubpart( + $this->subpartArray['###ORDERNAVIGATION###'], + '###SORT_LINK###' + ); + + // get sorting values (sortdate, title, what ever...) + $this->sortBy = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['sortByVisitor'], true); + } + + + /** + * The main entry point of this class + * It will return the complete sorting HTML + * @return string HTML + */ + public function renderSorting(&$fluidTemplateVariables) + { + // show sorting: + // if show Sorting is activated in FlexForm + // if a value to sortBy is set in FlexForm (title, relevance, sortdate, what ever...) + // if there are any entries in current search results + if ($this->conf['showSortInFrontend'] && !empty($this->conf['sortByVisitor']) && $this->pObj->numberOfResults) { + // loop all allowed orderings + foreach ($this->sortBy as $field) { + // we can't sort by score if there is no sword given + if ($this->pObj->sword != '' || $field != 'score') { + $sortByDir = $this->getDefaultSortingDirection($field); + $dbOrdering = TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(' ', $this->db->getOrdering()); + + /* if ordering direction is the same change it + * + * Explanation: + * No ordering is active. Default Ordering by db is "sortdate desc". + * Default ordering by current field is also "sortdate desc". + * So...if you click the link for sortdate it will sort the results by "sortdate desc" again + * To prevent this we change the default ordering here + */ + if ($field == $dbOrdering[0] && $sortByDir == $dbOrdering[1]) { + $sortByDir = $this->changeOrdering($sortByDir); + } + + $markerArray['###FIELDNAME###'] = $field; + $markerArray['###URL###'] = $url = $this->generateSortingLink($field, $sortByDir); + $markerArray['###CLASS###'] = $classname = $this->getClassNameForUpDownArrow($field, $dbOrdering); + + $links .= $this->cObj->substituteMarkerArray($this->subpartArray['###SORT_LINK###'], $markerArray); + + $fluidTemplateVariables['sortingLinks'][] = array( + 'field' => $field, + 'url' => $url, + 'class' => $classname + ); + } + } + + $content = $this->cObj->substituteSubpart( + $this->subpartArray['###ORDERNAVIGATION###'], + '###SORT_LINK###', + $links + ); + + $content = $this->cObj->substituteMarker( + $content, + '###LABEL_SORT###', + $this->pObj->pi_getLL('label_sort') + ); + + return $content; + } else { + return ''; + } + } + + + /** + * get default sorting direction + * f.e. default sorting for sortdate should be DESC. The most current records at first + * f.e. default sorting for relevance should be DESC. The best relevance at first + * f.e. default sorting for title should be ASC. Alphabetic order begins with A. + * @param string The field name to sort by + * @return string The default sorting (asc/desc) for given field + */ + public function getDefaultSortingDirection($field) + { + if (!empty($field) && is_string($field)) { + switch ($field) { + case 'sortdate': + case 'score': + $orderBy = 'desc'; + break; + case 'title': + default: + $orderBy = 'asc'; + break; + } + return $orderBy; + } else { + return 'asc'; + } + } + + + /** + * change ordering + * f.e. asc to desc and desc to asc + * @param string $direction asc or desc + * @return string desc or asc. If you call this function with a not allowed string, exactly this + * string will be returned. Short: The function do nothing + */ + public function changeOrdering($direction) + { + $allowedDirections = array('asc', 'desc'); + $direction = strtolower($direction); + $isInArray = TYPO3\CMS\Core\Utility\GeneralUtility::inArray($allowedDirections, $direction); + if (!empty($direction) && $isInArray) { + if ($direction == 'asc') { + $direction = 'desc'; + } else { + $direction = 'asc'; + } + } + return $direction; + } + + + /** + * get a class name for up and down arrows of sorting links + * @param string $field current field to sort by + * @param array $dbOrdering An array containing the field and ordering of current DB Ordering + * @return string The class name + */ + public function getClassNameForUpDownArrow($field, $dbOrdering) + { + $className = ''; + if (is_array($dbOrdering) && count($dbOrdering)) { + if ($field == $dbOrdering[0]) { + if ($dbOrdering[1] == 'asc') { + $className = 'up'; + } else { + $className = 'down'; + } + } + } + return $className; + } + + + /** + * generate the link for the given sorting value + * @param string $field + * @param string $sortByDir + * @return string The complete link as A-Tag + */ + public function generateSortingLink($field, $sortByDir) + { + $params = array(); + $params['sortByField'] = $field; + $params['sortByDir'] = $sortByDir; + + foreach ($params as $key => $value) { + $params[$key] = $this->cObj->wrap($value, $this->pObj->prefixId . '[' . $key . ']=|'); + } + + $conf = array(); + $conf['parameter'] = $GLOBALS['TSFE']->id; + $conf['addQueryString'] = '1'; + $conf['addQueryString.']['exclude'] = 'id,tx_kesearch_pi1[multi],cHash'; + $conf['additionalParams'] = '&' . implode('&', $params); + + return $this->cObj->typoLink( + $this->pObj->pi_getLL('orderlink_' . $field, $field), + $conf + ); + } } diff --git a/Classes/lib/filters/class.tx_kesearch_lib_filters_textlinks.php b/Classes/lib/filters/class.tx_kesearch_lib_filters_textlinks.php index b93c5249..0eb5ca23 100644 --- a/Classes/lib/filters/class.tx_kesearch_lib_filters_textlinks.php +++ b/Classes/lib/filters/class.tx_kesearch_lib_filters_textlinks.php @@ -1,374 +1,395 @@ init($pObj); - } - - - /** - * Initializes this object - * - * @param tx_kesearch_lib $pObj - * @return void - */ - public function init(tx_kesearch_lib $pObj) { - $this->pObj = $pObj; - $this->cObj = $this->pObj->cObj; - $this->conf = $this->pObj->conf; - - // reset global values - $this->countActiveOptions = 0; - $this->contentOfHiddenFields = array(); - $this->contentOfActiveOptions = array(); - $this->contentOfNormalOptions = array(); - - // set template array - $this->templateArray['filter'] = $this->cObj->getSubpart($this->pObj->templateCode, '###SUB_FILTER_TEXTLINKS###'); - $this->templateArray['options'] = $this->cObj->getSubpart($this->pObj->templateCode, '###SUB_FILTER_TEXTLINK_OPTION###'); - } - - - /** - * The main entry point of this class - * It will return the complete HTML for textlinks - * - * @param integer $filterUid uid of the current loopd filter - * @param array $optionsOfSearchresult All found options in current search result - * @param object $lib - * @return string HTML - */ - public function renderTextlinks($filterUid, $optionsOfSearchresult, $lib) { - $filters = $this->pObj->filters->getFilters(); - $filter = $filters[$filterUid]; - - if(!is_array($filter) || count($filter) == 0) return ''; - - // get options - $optionsOfFilter = $this->getOptionsOfFilter($filter, $optionsOfSearchresult); - if(!is_array($optionsOfFilter) || count($optionsOfFilter) == 0) return ''; - - // alphabetical sorting of filter options - if ($filter['alphabeticalsorting'] == 1) { - $this->pObj->sortArrayByColumn($optionsOfFilter, 'title'); - } - - $this->maxAllowedNormalOptions = $filter['amount']; - - if(is_array($this->pObj->piVars['filter'][$filterUid]) && count($this->pObj->piVars['filter'][$filterUid])) { - $piVarsOptionList = implode(',', array_keys($this->pObj->piVars['filter'][$filterUid])); - $optionsOfFilter = $this->pObj->getFilterOptions($piVarsOptionList); - } - - foreach($optionsOfFilter as $key => $data) { - $this->saveRenderedTextlinkToGlobalArrays($filterUid, $data); - } - - if(is_array($this->contentOfActiveOptions) && count($this->contentOfActiveOptions)) { - foreach($this->contentOfActiveOptions as $option) { - $contentOptions .= $option; - } - } else { - foreach($this->contentOfNormalOptions as $option) { - $contentOptions .= $option; - } - } - - // modify filter options by hook - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $contentOptions .= $_procObj->modifyFilterOptions( - $filterUid, - $contentOptions, - count($optionsOfFilter), - $this - ); - } - } - - unset($markerArray); - - // render filter - $contentFilters = $this->cObj->substituteSubpart( - $this->templateArray['filter'], - '###SUB_FILTER_TEXTLINK_OPTION###', - $contentOptions - ); - - // get title - $filterTitle = $filter['title']; - $filter['target_pid'] = ($filter['target_pid']) ? $filter['target_pid'] : $this->conf['resultPage']; - - // fill markers - $markerArray['###FILTERTITLE###'] = htmlspecialchars($filterTitle); - $markerArray['###HIDDEN_FIELDS###'] = implode(CHR(10), $this->contentOfHiddenFields); - - $exclude = 'tx_kesearch_pi1[page],tx_kesearch_pi1[multi],tx_kesearch_pi1[filter][' . $filterUid . ']'; - - if($this->countActiveOptions) { - $markerArray['###LINK_MULTISELECT###'] = ''; - $markerArray['###LINK_RESET_FILTER###'] = $this->cObj->typoLink( - $this->pObj->pi_getLL('reset_filter'), - array( - 'parameter' => $this->conf['resultPage'], - 'addQueryString' => 1, - 'addQueryString.' => array( - 'exclude' => $exclude - ) - ) - ); - } else { - // check if there is a special translation for current filter - $linkTextMore = $this->pObj->pi_getLL('linktext_more_' . $filterUid, $this->pObj->pi_getLL('linktext_more')); - $markerArray['###LINK_MULTISELECT###'] = $this->cObj->typoLink( - sprintf($linkTextMore, $filterTitle), - array( - 'parameter' => $filter['target_pid'], - 'addQueryString' => 1, - 'addQueryString.' => array( - 'exclude' => 'id,tx_kesearch_pi1[page],tx_kesearch_pi1[multi]' - ) - ) - ); - $markerArray['###LINK_RESET_FILTER###'] = ''; - } - - $contentFilters = $this->cObj->substituteMarkerArray($contentFilters, $markerArray); - return $contentFilters; - } - - - /** - * get options of given filter and regarding current search result - * Only options which are also found in result will be returned - * - * @param array $filter The current looped filter - * @param array $additionalOptionValues This is an array with some additional informations for each filteroption (title, tag, amount of records, selected) - * @return array A merged, sorted and complete Array with all option values we need - */ - public function getOptionsOfFilter($filter, $additionalOptionValues) { - if(is_array($filter) && count($filter) && is_array($additionalOptionValues)) { - // get all options - $filters = $this->pObj->filters->getFilters(); - $allOptionsOfCurrentFilter = $filters[$filter['uid']]['options']; - // build intersection of both arrays - $optionsOfCurrentFilter = array_intersect_key($allOptionsOfCurrentFilter, $additionalOptionValues); - // merge additional values into option array - \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($optionsOfCurrentFilter, (array)$additionalOptionValues); - // return sorted option array - return $this->sortMultiDimArray($optionsOfCurrentFilter); - } else return array(); - } - - - /** - * Sort multidimensional array - * Hint: This method keeps the original Array keys - * - * @param array $optionArray - * @return array Array with original Keys - */ - public function sortMultiDimArray($optionArray) { - if(is_array($optionArray) && count($optionArray)) { - // temporary saving all keys - $allOptionKeys = array_keys($optionArray); - - // prepare our array for sorting - foreach((array)$optionArray as $key => $array) { - $results[$key] = $array['results']; - $tags[$key] = $array['tag']; - } - - // sort multidim array - array_multisort($results, SORT_DESC, SORT_NUMERIC, $tags, SORT_ASC, SORT_STRING, $allOptionKeys, SORT_DESC, SORT_NUMERIC, $optionArray); - - // after multisort all keys are 0,1,2,3. So we have to restore our old keys - if(count($allOptionKeys) && count(array_values($optionArray))) { - return array_combine($allOptionKeys, array_values($optionArray)); - } else { - return array(); - } - } else { - return array(); - } - } - - - /** - * a little factory to decide which kind of textlink has to be rendered - * and save the result to global arrays - * - * @param integer $filterUid The filter uid - * @param array $option An array containing the current option record - * @return string The rendered text link - */ - public function saveRenderedTextlinkToGlobalArrays($filterUid, $option) { - if($this->pObj->piVars['filter'][$filterUid][$option['uid']]) { - $this->contentOfActiveOptions[] = $this->renderTextlinkForActiveOption($filterUid, $option); - } elseif(empty($this->pObj->piVars['filter'][$filterUid])) { - if(count($this->contentOfNormalOptions) < intval($this->maxAllowedNormalOptions)) { - $this->contentOfNormalOptions[] = $this->renderTextlinkForNormalOption($filterUid, $option); - } - } else return ''; - } - - - /** - * render textlink for active option - * - * @param integer $filterUid The filter uid - * @param array $option An array containing the option - * @return string A HTML formatted textlink - */ - public function renderTextlinkForActiveOption($filterUid, $option) { - // if multi is set AND option(s) of current filter is set by piVars - // then more than one entry can be selected - //if($this->pObj->piVars['multi'] && $this->pObj->piVars['filter'][$filterUid][$option['uid']]) { - $this->countActiveOptions++; - $markerArray['###CLASS###'] = 'active'; - $markerArray['###TEXTLINK###'] = htmlspecialchars($option['title']); - $this->contentOfHiddenFields[] = $this->renderHiddenField($filterUid, $option); - return $this->cObj->substituteMarkerArray($this->templateArray['options'], $markerArray); - } - - - /** - * render textlink for normal option - * - * @param integer $filterUid The filter uid - * @param array $option An array containing the option - * @return string A HTML formatted textlink - */ - public function renderTextlinkForNormalOption($filterUid, $option) { - // if multi is set AND option(s) of current filter is set by piVars - // then more than one entry can be selected - //if($this->pObj->piVars['multi'] && $this->pObj->piVars['filter'][$filterUid][$option['uid']]) { - $markerArray['###CLASS###'] = 'normal'; - $markerArray['###TEXTLINK###'] = $this->generateLink($filterUid, $option); - return $this->cObj->substituteMarkerArray($this->templateArray['options'], $markerArray); - } - - - /** - * generate the link for normal textlinks - * - * @param string $filterUid - * @param string $option - * @return string The complete link as A-Tag - */ - public function generateLink($filterUid, $option) { - $filters = $this->pObj->filters->getFilters(); - $params = array(); - $params[] = '[page]=1'; - $params[] = '[filter][' . $filterUid . '][' . $option['uid'] . ']=' . $option['tag']; - - $excludes = array(); - $excludes[] = 'id'; - $excludes[] = 'tx_kesearch_pi1[multi]'; - - // hook: modifyParamsForTextlinks - // This is useful if you want to define special sortings for each textlink - if(is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyParamsForTextlinks'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyParamsForTextlinks'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $_procObj->modifyParamsForTextlinks($params, $excludes, $option, $this->conf, $this->pObj); - } - } - - foreach($params as $key => $value) { - $params[$key] = $this->cObj->wrap($value, $this->pObj->prefixId . '|'); - } - - $conf = array(); - $conf['parameter'] = $this->conf['resultPage']; - $conf['addQueryString'] = '1'; - $conf['addQueryString.']['exclude'] = implode(',', $excludes); - $conf['additionalParams'] = '&' . implode('&', $params); - - $number_of_results = $this->pObj->renderNumberOfResultsString($option['results'], $filters[$filterUid]); - - return $this->cObj->typoLink($option['title'] . $number_of_results, $conf); - } - - - /** - * render a hidden field - * They are needed to not forget setted options while search for another word - * - * @param integer $filterUid The filter uid - * @param array $option An array containing the option - * @return string A HTML formatted hidden input field - */ - public function renderHiddenField($filterUid, $option) { - $attributes = array(); - $attributes['type'] = 'hidden'; - $attributes['name'] = 'tx_kesearch_pi1[filter][' . $filterUid . '][' . $option['uid'] . ']'; - $attributes['id'] = 'tx_kesearch_pi1_' . $filterUid . '_' . $option['uid']; - $attributes['value'] = htmlspecialchars($option['tag']); - - foreach($attributes as $key => $attribut) { - $attributes[$key] = $key . $this->cObj->wrap($attribut, '="|"'); - } - - return ''; - } -} \ No newline at end of file +class tx_kesearch_lib_filters_textlinks +{ + + public $conf = array(); + public $templateArray = array(); + public $countActiveOptions = 0; + public $contentOfHiddenFields = array(); + public $maxAllowedNormalOptions = 0; + public $contentOfActiveOptions = array(); + public $contentOfNormalOptions = array(); + + /** + * @var tx_kesearch_lib + */ + public $pObj; + + /** + * @var tslib_cObj + */ + public $cObj; + + /** + * The constructor of this class + * @param tx_kesearch_lib $pObj + */ + public function __construct(tx_kesearch_lib $pObj) + { + // initializes this object + $this->init($pObj); + } + + + /** + * Initializes this object + * @param tx_kesearch_lib $pObj + * @return void + */ + public function init(tx_kesearch_lib $pObj) + { + $this->pObj = $pObj; + $this->cObj = $this->pObj->cObj; + $this->conf = $this->pObj->conf; + + // reset global values + $this->countActiveOptions = 0; + $this->contentOfHiddenFields = array(); + $this->contentOfActiveOptions = array(); + $this->contentOfNormalOptions = array(); + + // set template array + $this->templateArray['filter'] = $this->cObj->getSubpart( + $this->pObj->templateCode, + '###SUB_FILTER_TEXTLINKS###' + ); + $this->templateArray['options'] = $this->cObj->getSubpart( + $this->pObj->templateCode, + '###SUB_FILTER_TEXTLINK_OPTION###' + ); + } + + + /** + * The main entry point of this class + * It will return the complete HTML for textlinks + * @param integer $filterUid uid of the current loopd filter + * @param array $optionsOfSearchresult All found options in current search result + * @param object $lib + * @return string HTML + */ + public function renderTextlinks($filterUid, $optionsOfSearchresult, $lib) + { + $filters = $this->pObj->filters->getFilters(); + $filter = $filters[$filterUid]; + + if (!is_array($filter) || count($filter) == 0) { + return ''; + } + + // get options + $optionsOfFilter = $this->getOptionsOfFilter($filter, $optionsOfSearchresult); + if (!is_array($optionsOfFilter) || count($optionsOfFilter) == 0) { + return ''; + } + + // alphabetical sorting of filter options + if ($filter['alphabeticalsorting'] == 1) { + $this->pObj->sortArrayByColumn($optionsOfFilter, 'title'); + } + + $this->maxAllowedNormalOptions = $filter['amount']; + + if (is_array($this->pObj->piVars['filter'][$filterUid]) && count($this->pObj->piVars['filter'][$filterUid])) { + $piVarsOptionList = implode(',', array_keys($this->pObj->piVars['filter'][$filterUid])); + $optionsOfFilter = $this->pObj->getFilterOptions($piVarsOptionList); + } + + foreach ($optionsOfFilter as $key => $data) { + $this->saveRenderedTextlinkToGlobalArrays($filterUid, $data); + } + + if (is_array($this->contentOfActiveOptions) && count($this->contentOfActiveOptions)) { + foreach ($this->contentOfActiveOptions as $option) { + $contentOptions .= $option; + } + } else { + foreach ($this->contentOfNormalOptions as $option) { + $contentOptions .= $option; + } + } + + // modify filter options by hook + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyFilterOptions'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $contentOptions .= $_procObj->modifyFilterOptions( + $filterUid, + $contentOptions, + count($optionsOfFilter), + $this + ); + } + } + + unset($markerArray); + + // render filter + $contentFilters = $this->cObj->substituteSubpart( + $this->templateArray['filter'], + '###SUB_FILTER_TEXTLINK_OPTION###', + $contentOptions + ); + + // get title + $filterTitle = $filter['title']; + $filter['target_pid'] = ($filter['target_pid']) ? $filter['target_pid'] : $this->conf['resultPage']; + + // fill markers + $markerArray['###FILTERTITLE###'] = htmlspecialchars($filterTitle); + $markerArray['###HIDDEN_FIELDS###'] = implode(CHR(10), $this->contentOfHiddenFields); + + $exclude = 'tx_kesearch_pi1[page],tx_kesearch_pi1[multi],tx_kesearch_pi1[filter][' . $filterUid . ']'; + + if ($this->countActiveOptions) { + $markerArray['###LINK_MULTISELECT###'] = ''; + $markerArray['###LINK_RESET_FILTER###'] = $this->cObj->typoLink( + $this->pObj->pi_getLL('reset_filter'), + array( + 'parameter' => $this->conf['resultPage'], + 'addQueryString' => 1, + 'addQueryString.' => array( + 'exclude' => $exclude + ) + ) + ); + } else { + // check if there is a special translation for current filter + $linkTextMore = $this->pObj->pi_getLL( + 'linktext_more_' . $filterUid, + $this->pObj->pi_getLL('linktext_more') + ); + $markerArray['###LINK_MULTISELECT###'] = $this->cObj->typoLink( + sprintf($linkTextMore, $filterTitle), + array( + 'parameter' => $filter['target_pid'], + 'addQueryString' => 1, + 'addQueryString.' => array( + 'exclude' => 'id,tx_kesearch_pi1[page],tx_kesearch_pi1[multi]' + ) + ) + ); + $markerArray['###LINK_RESET_FILTER###'] = ''; + } + + $contentFilters = $this->cObj->substituteMarkerArray($contentFilters, $markerArray); + return $contentFilters; + } + + + /** + * get options of given filter and regarding current search result + * Only options which are also found in result will be returned + * @param array $filter The current looped filter + * @param array $additionalOptionValues This is an array with some additional informations for each filteroption (title, tag, amount of records, selected) + * @return array A merged, sorted and complete Array with all option values we need + */ + public function getOptionsOfFilter($filter, $additionalOptionValues) + { + if (is_array($filter) && count($filter) && is_array($additionalOptionValues)) { + // get all options + $filters = $this->pObj->filters->getFilters(); + $allOptionsOfCurrentFilter = $filters[$filter['uid']]['options']; + // build intersection of both arrays + $optionsOfCurrentFilter = array_intersect_key($allOptionsOfCurrentFilter, $additionalOptionValues); + // merge additional values into option array + \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule( + $optionsOfCurrentFilter, + (array)$additionalOptionValues + ); + // return sorted option array + return $this->sortMultiDimArray($optionsOfCurrentFilter); + } else { + return array(); + } + } + + + /** + * Sort multidimensional array + * Hint: This method keeps the original Array keys + * @param array $optionArray + * @return array Array with original Keys + */ + public function sortMultiDimArray($optionArray) + { + if (is_array($optionArray) && count($optionArray)) { + // temporary saving all keys + $allOptionKeys = array_keys($optionArray); + + // prepare our array for sorting + foreach ((array)$optionArray as $key => $array) { + $results[$key] = $array['results']; + $tags[$key] = $array['tag']; + } + + // sort multidim array + array_multisort( + $results, + SORT_DESC, + SORT_NUMERIC, + $tags, + SORT_ASC, + SORT_STRING, + $allOptionKeys, + SORT_DESC, + SORT_NUMERIC, + $optionArray + ); + + // after multisort all keys are 0,1,2,3. So we have to restore our old keys + if (count($allOptionKeys) && count(array_values($optionArray))) { + return array_combine($allOptionKeys, array_values($optionArray)); + } else { + return array(); + } + } else { + return array(); + } + } + + + /** + * a little factory to decide which kind of textlink has to be rendered + * and save the result to global arrays + * @param integer $filterUid The filter uid + * @param array $option An array containing the current option record + * @return string The rendered text link + */ + public function saveRenderedTextlinkToGlobalArrays($filterUid, $option) + { + if ($this->pObj->piVars['filter'][$filterUid][$option['uid']]) { + $this->contentOfActiveOptions[] = $this->renderTextlinkForActiveOption($filterUid, $option); + } elseif (empty($this->pObj->piVars['filter'][$filterUid])) { + if (count($this->contentOfNormalOptions) < intval($this->maxAllowedNormalOptions)) { + $this->contentOfNormalOptions[] = $this->renderTextlinkForNormalOption($filterUid, $option); + } + } else { + return ''; + } + } + + + /** + * render textlink for active option + * @param integer $filterUid The filter uid + * @param array $option An array containing the option + * @return string A HTML formatted textlink + */ + public function renderTextlinkForActiveOption($filterUid, $option) + { + // if multi is set AND option(s) of current filter is set by piVars + // then more than one entry can be selected + //if($this->pObj->piVars['multi'] && $this->pObj->piVars['filter'][$filterUid][$option['uid']]) { + $this->countActiveOptions++; + $markerArray['###CLASS###'] = 'active'; + $markerArray['###TEXTLINK###'] = htmlspecialchars($option['title']); + $this->contentOfHiddenFields[] = $this->renderHiddenField($filterUid, $option); + return $this->cObj->substituteMarkerArray($this->templateArray['options'], $markerArray); + } + + + /** + * render textlink for normal option + * @param integer $filterUid The filter uid + * @param array $option An array containing the option + * @return string A HTML formatted textlink + */ + public function renderTextlinkForNormalOption($filterUid, $option) + { + // if multi is set AND option(s) of current filter is set by piVars + // then more than one entry can be selected + //if($this->pObj->piVars['multi'] && $this->pObj->piVars['filter'][$filterUid][$option['uid']]) { + $markerArray['###CLASS###'] = 'normal'; + $markerArray['###TEXTLINK###'] = $this->generateLink($filterUid, $option); + return $this->cObj->substituteMarkerArray($this->templateArray['options'], $markerArray); + } + + + /** + * generate the link for normal textlinks + * @param string $filterUid + * @param string $option + * @return string The complete link as A-Tag + */ + public function generateLink($filterUid, $option) + { + $filters = $this->pObj->filters->getFilters(); + $params = array(); + $params[] = '[page]=1'; + $params[] = '[filter][' . $filterUid . '][' . $option['uid'] . ']=' . $option['tag']; + + $excludes = array(); + $excludes[] = 'id'; + $excludes[] = 'tx_kesearch_pi1[multi]'; + + // hook: modifyParamsForTextlinks + // This is useful if you want to define special sortings for each textlink + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyParamsForTextlinks'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyParamsForTextlinks'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->modifyParamsForTextlinks($params, $excludes, $option, $this->conf, $this->pObj); + } + } + + foreach ($params as $key => $value) { + $params[$key] = $this->cObj->wrap($value, $this->pObj->prefixId . '|'); + } + + $conf = array(); + $conf['parameter'] = $this->conf['resultPage']; + $conf['addQueryString'] = '1'; + $conf['addQueryString.']['exclude'] = implode(',', $excludes); + $conf['additionalParams'] = '&' . implode('&', $params); + + $number_of_results = $this->pObj->renderNumberOfResultsString($option['results'], $filters[$filterUid]); + + return $this->cObj->typoLink($option['title'] . $number_of_results, $conf); + } + + /** + * render a hidden field + * They are needed to not forget setted options while search for another word + * @param integer $filterUid The filter uid + * @param array $option An array containing the option + * @return string A HTML formatted hidden input field + */ + public function renderHiddenField($filterUid, $option) + { + $attributes = array(); + $attributes['type'] = 'hidden'; + $attributes['name'] = 'tx_kesearch_pi1[filter][' . $filterUid . '][' . $option['uid'] . ']'; + $attributes['id'] = 'tx_kesearch_pi1_' . $filterUid . '_' . $option['uid']; + $attributes['value'] = htmlspecialchars($option['tag']); + + foreach ($attributes as $key => $attribut) { + $attributes[$key] = $key . $this->cObj->wrap($attribut, '="|"'); + } + + return ''; + } +} diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index acd8a7b8..a5242c47 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -3,4 +3,4 @@ 'ke_search', 'Configuration/TypoScript', 'Faceted Search' -); \ No newline at end of file +); diff --git a/Configuration/TCA/tx_kesearch_filteroptions.php b/Configuration/TCA/tx_kesearch_filteroptions.php index 8d20c54d..7a66fe3f 100644 --- a/Configuration/TCA/tx_kesearch_filteroptions.php +++ b/Configuration/TCA/tx_kesearch_filteroptions.php @@ -124,4 +124,4 @@ 'palettes' => array( '1' => array('showitem' => '') ) -); \ No newline at end of file +); diff --git a/Tests/indexer/class.tx_kesearch_indexerTest.php b/Tests/indexer/class.tx_kesearch_indexerTest.php index 768cef4d..8d7aab09 100644 --- a/Tests/indexer/class.tx_kesearch_indexerTest.php +++ b/Tests/indexer/class.tx_kesearch_indexerTest.php @@ -1,79 +1,76 @@ indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); - $this->indexer->additionalFields = array('orig_uid', 'orig_pid', 'enddate'); - - } - - public function tearDown() { - unset($this->indexer); - } - - - - - - /** - * Test additional query parts for additional fields - * - * @test - */ - public function checkGetQueryPartsForAdditionalFields() { - $now = time(); - $fieldValues = array( - 'tstamp' => $now, - 'crdate' => $now, - 'title' => 'tolle Überschrift', - 'orig_uid' => 213, - 'orig_pid' => 423, - 'enddate' => $now, - ); - $fieldValues = $GLOBALS['TYPO3_DB']->fullQuoteArray($fieldValues, 'tx_kesearch_index'); - - $shouldArray = array( - 'set' => ', @orig_uid = \'213\', @orig_pid = \'423\', @enddate = \'' . $now . '\'', - 'execute' => ', @orig_uid, @orig_pid, @enddate' - ); - - $isArray = $this->indexer->getQueryPartForAdditionalFields($fieldValues); - - $this->assertEquals($shouldArray, $isArray); - } - - - /** - * Test additional query parts for additional fields - * - * @test - */ - public function getTagTest() { - $fields = 'uid, title, tag'; - $table = 'tx_kesearch_filteroptions'; - $where = '1=1 '; - $where .= t3lib_befunc::BEenableFields($table, 0); - $where .= t3lib_befunc::deleteClause($table, 0); - - $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow( - $fields, - $table, - $where - ); - if(is_array($row) && count($row)) { - $return = $this->indexer->getTag($row['uid'], false); - $this->assertEquals($row['tag'], $return); - $return = $this->indexer->getTag($row['uid'], true); - $this->assertEquals($row['title'], $return); - } - } -} \ No newline at end of file +class IndexerTest extends Tx_Extbase_BaseTestCase +{ + + /** + * @var tx_kesearch_indexer + */ + public $indexer; + + + public function setUp() + { + $this->indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); + $this->indexer->additionalFields = array('orig_uid', 'orig_pid', 'enddate'); + } + + public function tearDown() + { + unset($this->indexer); + } + + + /** + * Test additional query parts for additional fields + * @test + */ + public function checkGetQueryPartsForAdditionalFields() + { + $now = time(); + $fieldValues = array( + 'tstamp' => $now, + 'crdate' => $now, + 'title' => 'tolle Überschrift', + 'orig_uid' => 213, + 'orig_pid' => 423, + 'enddate' => $now, + ); + $fieldValues = $GLOBALS['TYPO3_DB']->fullQuoteArray($fieldValues, 'tx_kesearch_index'); + + $shouldArray = array( + 'set' => ', @orig_uid = \'213\', @orig_pid = \'423\', @enddate = \'' . $now . '\'', + 'execute' => ', @orig_uid, @orig_pid, @enddate' + ); + + $isArray = $this->indexer->getQueryPartForAdditionalFields($fieldValues); + + $this->assertEquals($shouldArray, $isArray); + } + + + /** + * Test additional query parts for additional fields + * @test + */ + public function getTagTest() + { + $fields = 'uid, title, tag'; + $table = 'tx_kesearch_filteroptions'; + $where = '1=1 '; + $where .= t3lib_befunc::BEenableFields($table, 0); + $where .= t3lib_befunc::deleteClause($table, 0); + + $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow( + $fields, + $table, + $where + ); + if (is_array($row) && count($row)) { + $return = $this->indexer->getTag($row['uid'], false); + $this->assertEquals($row['tag'], $return); + $return = $this->indexer->getTag($row['uid'], true); + $this->assertEquals($row['title'], $return); + } + } +} diff --git a/Tests/indexer/class.tx_kesearch_indexerTypesTest.php b/Tests/indexer/class.tx_kesearch_indexerTypesTest.php index c1e27d42..f6a841f8 100644 --- a/Tests/indexer/class.tx_kesearch_indexerTypesTest.php +++ b/Tests/indexer/class.tx_kesearch_indexerTypesTest.php @@ -1,168 +1,182 @@ indexerTypes = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer_types'); - $this->indexerTypes->queryGen = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('t3lib_queryGenerator'); - $this->pageIndexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer_types_page'); - $this->pageIndexer->pObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); - $this->pageIndexer->pObj->extConf['prePostTagChar'] = '#'; - } - - public function tearDown() { - unset($this->indexerTypes); - unset($this->pageIndexer); - } - - - - - - /** - * Test method getPagelist - * - * @test - */ - public function getPagelistTest() { - $pidArray = $this->indexerTypes->getPagelist(); - // check if it is of type array - $this->assertInternalType('array', $pidArray); - // this is the recursive part, so it should have 2 or more entries - $this->assertEquals(0, count($pidArray)); - - // get the rootPage UID. In most cases it should have recursive child elements - $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - 'uid', - 'pages', - 'deleted=0 AND hidden=0 AND is_siteroot=1', - '', '', '1' - ); - if(count($rows) > 0) { - $rootPage = $rows[0]['uid']; - } else $rootPage = 1; - - $pidArray = $this->indexerTypes->getPagelist($rootPage); - // check if it is of type array - $this->assertInternalType('array', $pidArray); - // this is the recursive part, so it should have 2 or more entries - $this->assertGreaterThanOrEqual(2, count($pidArray)); - - $pidArray = $this->indexerTypes->getPagelist('', $rootPage); - // check if it is of type array - $this->assertInternalType('array', $pidArray); - // this is the recursive part, so it should have 2 or more entries - $this->assertEquals(1, count($pidArray)); - } - - - /** - * Test method getPageRecords - * - * @test - */ - public function getPageRecordsTest() { - // get the rootPage UID. In most cases it should have recursive child elements - $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - 'uid', - 'pages', - 'deleted=0 AND hidden=0 AND is_siteroot=1', - '', '', '1' - ); - if(count($rows) > 0) { - $rootPage = $rows[0]['uid']; - } else $rootPage = 1; - - $pidArray = $this->indexerTypes->getPagelist($rootPage); - $pageRecords = $this->indexerTypes->getPageRecords($pidArray); - // check if it is of type array - $this->assertInternalType('array', $pageRecords); - // there should be at last 1 record - $this->assertGreaterThanOrEqual(1, count($pageRecords)); - // check for some array keys which have to be present - $this->assertArrayHasKey('uid', $pageRecords[$rootPage]); - $this->assertArrayHasKey('title', $pageRecords[$rootPage]); - $this->assertNotEmpty($pageRecords[$rootPage]['uid']); - $this->assertNotEmpty($pageRecords[$rootPage]['title']); - } - - - /** - * Test method getPidList - * - * @test - */ - public function getPidListTest() { - // get the rootPage UID. In most cases it should have recursive child elements - $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - 'uid', - 'pages', - 'deleted=0 AND hidden=0 AND is_siteroot=1', - '', '', '1' - ); - if(count($rows) > 0) { - $rootPage = $rows[0]['uid']; - } else $rootPage = 1; - - $pidArray = $this->indexerTypes->getPidList($rootPage, '', 'tt_news'); - // check if it is of type array - $this->assertInternalType('array', $pidArray); - // there should be at last 1 record - $this->assertGreaterThanOrEqual(1, count($pidArray)); - foreach($pidArray as $pid) { - $this->assertInternalType('integer', $pid); - } - - $pidArray = $this->indexerTypes->getPidList('', $rootPage, 'tt_news'); - // check if it is of type array - $this->assertInternalType('array', $pidArray); - // there should be 1 record - $this->assertEquals(1, count($pidArray)); - $this->assertInternalType('integer', $pidArray[0]); - } - - - /** - * Test method addTagsToPageRecords - * - * @test - */ - public function addTagsToPageRecordsTest() { - // get the rootPage UID. In most cases it should have recursive child elements - $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( - 'uid', - 'pages', - 'deleted=0 AND hidden=0 AND is_siteroot=1', - '', '', '1' - ); - if(count($rows) > 0) { - $rootPage = $rows[0]['uid']; - } else $rootPage = 1; - - // get all pages. Regardeless if they are shortcut, sysfolder or external link - $indexPids = $this->pageIndexer->getPagelist($rootPage); - - // add complete page record to list of pids in $indexPids - // and remove all page of type shortcut, sysfolder and external link - $this->pageIndexer->pageRecords = $this->pageIndexer->getPageRecords($indexPids); - - // create a new list of allowed pids - $indexPids = array_keys($this->pageIndexer->pageRecords); - - // add the tags of each page to the global page array - $this->pageIndexer->addTagsToPageRecords($indexPids); - } -} \ No newline at end of file + +class IndexerTypesTest extends Tx_Extbase_BaseTestCase +{ + + /** + * @var tx_kesearch_indexer_types_page + */ + public $pageIndexer; + + /** + * @var tx_kesearch_indexer_types + */ + public $indexerTypes; + + + public function setUp() + { + $this->indexerTypes = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer_types'); + $this->indexerTypes->queryGen = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('t3lib_queryGenerator'); + $this->pageIndexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer_types_page'); + $this->pageIndexer->pObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); + $this->pageIndexer->pObj->extConf['prePostTagChar'] = '#'; + } + + public function tearDown() + { + unset($this->indexerTypes); + unset($this->pageIndexer); + } + + + /** + * Test method getPagelist + * @test + */ + public function getPagelistTest() + { + $pidArray = $this->indexerTypes->getPagelist(); + // check if it is of type array + $this->assertInternalType('array', $pidArray); + // this is the recursive part, so it should have 2 or more entries + $this->assertEquals(0, count($pidArray)); + + // get the rootPage UID. In most cases it should have recursive child elements + $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'uid', + 'pages', + 'deleted=0 AND hidden=0 AND is_siteroot=1', + '', + '', + '1' + ); + if (count($rows) > 0) { + $rootPage = $rows[0]['uid']; + } else { + $rootPage = 1; + } + + $pidArray = $this->indexerTypes->getPagelist($rootPage); + // check if it is of type array + $this->assertInternalType('array', $pidArray); + // this is the recursive part, so it should have 2 or more entries + $this->assertGreaterThanOrEqual(2, count($pidArray)); + + $pidArray = $this->indexerTypes->getPagelist('', $rootPage); + // check if it is of type array + $this->assertInternalType('array', $pidArray); + // this is the recursive part, so it should have 2 or more entries + $this->assertEquals(1, count($pidArray)); + } + + + /** + * Test method getPageRecords + * @test + */ + public function getPageRecordsTest() + { + // get the rootPage UID. In most cases it should have recursive child elements + $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'uid', + 'pages', + 'deleted=0 AND hidden=0 AND is_siteroot=1', + '', + '', + '1' + ); + if (count($rows) > 0) { + $rootPage = $rows[0]['uid']; + } else { + $rootPage = 1; + } + + $pidArray = $this->indexerTypes->getPagelist($rootPage); + $pageRecords = $this->indexerTypes->getPageRecords($pidArray); + // check if it is of type array + $this->assertInternalType('array', $pageRecords); + // there should be at last 1 record + $this->assertGreaterThanOrEqual(1, count($pageRecords)); + // check for some array keys which have to be present + $this->assertArrayHasKey('uid', $pageRecords[$rootPage]); + $this->assertArrayHasKey('title', $pageRecords[$rootPage]); + $this->assertNotEmpty($pageRecords[$rootPage]['uid']); + $this->assertNotEmpty($pageRecords[$rootPage]['title']); + } + + + /** + * Test method getPidList + * @test + */ + public function getPidListTest() + { + // get the rootPage UID. In most cases it should have recursive child elements + $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'uid', + 'pages', + 'deleted=0 AND hidden=0 AND is_siteroot=1', + '', + '', + '1' + ); + if (count($rows) > 0) { + $rootPage = $rows[0]['uid']; + } else { + $rootPage = 1; + } + + $pidArray = $this->indexerTypes->getPidList($rootPage, '', 'tt_news'); + // check if it is of type array + $this->assertInternalType('array', $pidArray); + // there should be at last 1 record + $this->assertGreaterThanOrEqual(1, count($pidArray)); + foreach ($pidArray as $pid) { + $this->assertInternalType('integer', $pid); + } + + $pidArray = $this->indexerTypes->getPidList('', $rootPage, 'tt_news'); + // check if it is of type array + $this->assertInternalType('array', $pidArray); + // there should be 1 record + $this->assertEquals(1, count($pidArray)); + $this->assertInternalType('integer', $pidArray[0]); + } + + + /** + * Test method addTagsToPageRecords + * @test + */ + public function addTagsToPageRecordsTest() + { + // get the rootPage UID. In most cases it should have recursive child elements + $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'uid', + 'pages', + 'deleted=0 AND hidden=0 AND is_siteroot=1', + '', + '', + '1' + ); + if (count($rows) > 0) { + $rootPage = $rows[0]['uid']; + } else { + $rootPage = 1; + } + + // get all pages. Regardeless if they are shortcut, sysfolder or external link + $indexPids = $this->pageIndexer->getPagelist($rootPage); + + // add complete page record to list of pids in $indexPids + // and remove all page of type shortcut, sysfolder and external link + $this->pageIndexer->pageRecords = $this->pageIndexer->getPageRecords($indexPids); + + // create a new list of allowed pids + $indexPids = array_keys($this->pageIndexer->pageRecords); + + // add the tags of each page to the global page array + $this->pageIndexer->addTagsToPageRecords($indexPids); + } +} diff --git a/Tests/lib/class.tx_kesearch_dbOrderingTest.php b/Tests/lib/class.tx_kesearch_dbOrderingTest.php index 2e479246..b0cb3bf5 100644 --- a/Tests/lib/class.tx_kesearch_dbOrderingTest.php +++ b/Tests/lib/class.tx_kesearch_dbOrderingTest.php @@ -1,212 +1,208 @@ pObj = $this->getMock('tx_kesearch_pi1'); - $this->pObj->expects($this->any())->method('pi_getLL')->will($this->returnValue('Suchbegriff')); - - $this->div = new tx_kesearch_lib_div; - } - - public function tearDown() { - unset($this->pObj); - unset($this->div); - } - - - - - - /** - * Test ordering if no searchword was given - * - test in case of visible in FE - * - and not visible in FE - * - * @test - */ - public function checkOrderingWithoutSearchword() { - $this->pObj->sword = ''; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'sortdate desc', - 'showSortInFrontend' => false, - 'sortByAdmin' => 'sortdate desc' - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('sortdate desc', $db->getOrdering()); - - $this->pObj->sword = ''; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'sortdate desc', - 'showSortInFrontend' => true, - 'sortByAdmin' => 'sortdate desc' - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('sortdate desc', $db->getOrdering()); - } - - - /** - * Test ordering if a searchword was given - * - show sorting in FE is forbidden - * - admin presorts the result - * - * @test - */ - public function checkOrderingWithSearchwordPresortedByAdmin() { - $this->pObj->sword = 'Hallo'; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'tstamp asc', - 'showSortInFrontend' => false, - 'sortByAdmin' => 'sortdate desc' - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('sortdate desc', $db->getOrdering()); - } - - - /** - * Test ordering if a searchword was given - * - show sorting in FE is allowed - * - admin presorts are uninteresting - * - FE-User is allowed to choose between sortdate,tstamp and title - * - no piVars are given - * - * @test - */ - public function checkOrderingWithSearchwordAndUserCanSortWithoutPiVars() { - $this->pObj->sword = 'Hallo'; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'tstamp asc', - 'showSortInFrontend' => true, - 'sortByVisitor' => 'sortdate,tstamp,title', - 'sortByAdmin' => 'sortdate desc' - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('tstamp asc', $db->getOrdering()); - } - - - /** - * Test ordering if a searchword was given - * - show sorting in FE is allowed - * - admin presorts are uninteresting - * - FE-User is allowed to choose between sortdate,tstamp and title - * - unallowed piVars are given - * - * @test - */ - public function checkOrderingWithSearchwordAndUserCanSortWithUnallowedPiVars() { - $this->pObj->sword = 'Hallo'; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'tstamp asc', - 'showSortInFrontend' => true, - 'sortByVisitor' => 'sortdate,tstamp,title', - 'sortByAdmin' => 'sortdate desc' - ); - $this->pObj->piVars = array( - 'orderByField' => 'content', - 'orderByDir' => 'asc', - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('tstamp asc', $db->getOrdering()); - } - - - /** - * Test ordering if a searchword was given - * - show sorting in FE is allowed - * - admin presorts are uninteresting - * - FE-User is allowed to choose between sortdate,tstamp and title - * - piVars are given but orderDir is wrong - * - * @test - */ - public function checkOrderingWithSearchwordAndUserCanSortWithUnallowedPiVarForDirection() { - $this->pObj->sword = 'Hallo'; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'tstamp asc', - 'showSortInFrontend' => true, - 'sortByVisitor' => 'sortdate,tstamp,title', - 'sortByAdmin' => 'sortdate desc' - ); - $this->pObj->piVars = array( - 'orderByField' => 'title', - 'orderByDir' => 'trallala', - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - // orderdirections fallback is "asc" - $this->assertEquals('title asc', $db->getOrdering()); - } - - - /** - * Test ordering if a searchword was given - * - show sorting in FE is allowed - * - admin presorts are uninteresting - * - FE-User is allowed to choose between sortdate,tstamp and title - * - allowed piVars are given - * - * @test - */ - public function checkOrderingWithSearchwordAndUserCanSortWithAllowedPiVars() { - $this->pObj->sword = 'Hallo'; - $this->pObj->conf = array( - 'sortWithoutSearchword' => 'tstamp asc', - 'showSortInFrontend' => true, - 'sortByVisitor' => 'sortdate,tstamp,title', - 'sortByAdmin' => 'sortdate desc' - ); - $this->pObj->piVars = array( - 'orderByField' => 'title', - 'orderByDir' => 'asc', - ); - - $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); - - $db = new tx_kesearch_db($this->pObj); - - $this->assertEquals('title asc', $db->getOrdering()); - } - -} \ No newline at end of file + public function setUp() + { + $this->pObj = $this->getMock('tx_kesearch_pi1'); + $this->pObj->expects($this->any())->method('pi_getLL')->will($this->returnValue('Suchbegriff')); + + $this->div = new tx_kesearch_lib_div; + } + + public function tearDown() + { + unset($this->pObj); + unset($this->div); + } + + + /** + * Test ordering if no searchword was given + * - test in case of visible in FE + * - and not visible in FE + * @test + */ + public function checkOrderingWithoutSearchword() + { + $this->pObj->sword = ''; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'sortdate desc', + 'showSortInFrontend' => false, + 'sortByAdmin' => 'sortdate desc' + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('sortdate desc', $db->getOrdering()); + + $this->pObj->sword = ''; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'sortdate desc', + 'showSortInFrontend' => true, + 'sortByAdmin' => 'sortdate desc' + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('sortdate desc', $db->getOrdering()); + } + + + /** + * Test ordering if a searchword was given + * - show sorting in FE is forbidden + * - admin presorts the result + * @test + */ + public function checkOrderingWithSearchwordPresortedByAdmin() + { + $this->pObj->sword = 'Hallo'; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'tstamp asc', + 'showSortInFrontend' => false, + 'sortByAdmin' => 'sortdate desc' + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('sortdate desc', $db->getOrdering()); + } + + + /** + * Test ordering if a searchword was given + * - show sorting in FE is allowed + * - admin presorts are uninteresting + * - FE-User is allowed to choose between sortdate,tstamp and title + * - no piVars are given + * @test + */ + public function checkOrderingWithSearchwordAndUserCanSortWithoutPiVars() + { + $this->pObj->sword = 'Hallo'; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'tstamp asc', + 'showSortInFrontend' => true, + 'sortByVisitor' => 'sortdate,tstamp,title', + 'sortByAdmin' => 'sortdate desc' + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('tstamp asc', $db->getOrdering()); + } + + + /** + * Test ordering if a searchword was given + * - show sorting in FE is allowed + * - admin presorts are uninteresting + * - FE-User is allowed to choose between sortdate,tstamp and title + * - unallowed piVars are given + * @test + */ + public function checkOrderingWithSearchwordAndUserCanSortWithUnallowedPiVars() + { + $this->pObj->sword = 'Hallo'; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'tstamp asc', + 'showSortInFrontend' => true, + 'sortByVisitor' => 'sortdate,tstamp,title', + 'sortByAdmin' => 'sortdate desc' + ); + $this->pObj->piVars = array( + 'orderByField' => 'content', + 'orderByDir' => 'asc', + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('tstamp asc', $db->getOrdering()); + } + + + /** + * Test ordering if a searchword was given + * - show sorting in FE is allowed + * - admin presorts are uninteresting + * - FE-User is allowed to choose between sortdate,tstamp and title + * - piVars are given but orderDir is wrong + * @test + */ + public function checkOrderingWithSearchwordAndUserCanSortWithUnallowedPiVarForDirection() + { + $this->pObj->sword = 'Hallo'; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'tstamp asc', + 'showSortInFrontend' => true, + 'sortByVisitor' => 'sortdate,tstamp,title', + 'sortByAdmin' => 'sortdate desc' + ); + $this->pObj->piVars = array( + 'orderByField' => 'title', + 'orderByDir' => 'trallala', + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + // orderdirections fallback is "asc" + $this->assertEquals('title asc', $db->getOrdering()); + } + + + /** + * Test ordering if a searchword was given + * - show sorting in FE is allowed + * - admin presorts are uninteresting + * - FE-User is allowed to choose between sortdate,tstamp and title + * - allowed piVars are given + * @test + */ + public function checkOrderingWithSearchwordAndUserCanSortWithAllowedPiVars() + { + $this->pObj->sword = 'Hallo'; + $this->pObj->conf = array( + 'sortWithoutSearchword' => 'tstamp asc', + 'showSortInFrontend' => true, + 'sortByVisitor' => 'sortdate,tstamp,title', + 'sortByAdmin' => 'sortdate desc' + ); + $this->pObj->piVars = array( + 'orderByField' => 'title', + 'orderByDir' => 'asc', + ); + + $this->pObj->piVars = $this->div->cleanPiVars($this->pObj->piVars); + + $db = new tx_kesearch_db($this->pObj); + + $this->assertEquals('title asc', $db->getOrdering()); + } +} diff --git a/Tests/lib/class.tx_kesearch_libOrderingTest.php b/Tests/lib/class.tx_kesearch_libOrderingTest.php index 0c06e289..101898d1 100644 --- a/Tests/lib/class.tx_kesearch_libOrderingTest.php +++ b/Tests/lib/class.tx_kesearch_libOrderingTest.php @@ -1,54 +1,52 @@ div = new tx_kesearch_lib_div; - } - - public function tearDown() { - unset($this->div); - } - - - - - - /** - * Test ordering if no searchword was given - * - * @test - */ - public function checkOrderingWithoutNeededConditions() { - // Test with showSortInFrontend = false - $this->conf = array( - 'showSortInFrontend' => false, - 'sortByVisitor' => 'sortdate,title,tstamp', - ); - $this->numberOfResults = 35; - - $lib = new tx_kesearch_lib; - $this->assertEquals('', $lib->renderOrdering()); - - // Test with sortByVisitor = empty - $this->conf = array( - 'showSortInFrontend' => true, - 'sortByVisitor' => '', - ); - $this->numberOfResults = 35; - $this->assertEquals('', $lib->renderOrdering()); - - // Test with numberOfResults = 0 - $this->conf = array( - 'showSortInFrontend' => true, - 'sortByVisitor' => 'sortdate,title,tstamp', - ); - $this->numberOfResults = 0; - $this->assertEquals('', $lib->renderOrdering()); - } +class LibOrderingTest extends Tx_Extbase_BaseTestCase +{ + + public $conf = array(); + + + public function setUp() + { + $this->div = new tx_kesearch_lib_div; + } + + public function tearDown() + { + unset($this->div); + } + + + /** + * Test ordering if no searchword was given + * @test + */ + public function checkOrderingWithoutNeededConditions() + { + // Test with showSortInFrontend = false + $this->conf = array( + 'showSortInFrontend' => false, + 'sortByVisitor' => 'sortdate,title,tstamp', + ); + $this->numberOfResults = 35; + + $lib = new tx_kesearch_lib; + $this->assertEquals('', $lib->renderOrdering()); + + // Test with sortByVisitor = empty + $this->conf = array( + 'showSortInFrontend' => true, + 'sortByVisitor' => '', + ); + $this->numberOfResults = 35; + $this->assertEquals('', $lib->renderOrdering()); + + // Test with numberOfResults = 0 + $this->conf = array( + 'showSortInFrontend' => true, + 'sortByVisitor' => 'sortdate,title,tstamp', + ); + $this->numberOfResults = 0; + $this->assertEquals('', $lib->renderOrdering()); + } } \ No newline at end of file diff --git a/Tests/lib/class.tx_kesearch_lib_searchphraseTest.php b/Tests/lib/class.tx_kesearch_lib_searchphraseTest.php index b667a18c..24b517e5 100644 --- a/Tests/lib/class.tx_kesearch_lib_searchphraseTest.php +++ b/Tests/lib/class.tx_kesearch_lib_searchphraseTest.php @@ -1,155 +1,153 @@ getMock('tx_kesearch_lib'); + $searchLib->extConf['searchWordLength'] = 4; + $searchLib->expects($this->any()) + ->method('pi_getLL') + ->will($this->returnValue('Bitte geben Sie einen Suchbegriff ein')); + + $this->searchPhrase = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib_searchphrase'); + + $this->searchPhrase->initialize($searchLib); + } + + public function tearDown() + { + unset($this->searchPhrase); + } + /** + * Test checkAgainstDefaultValue + * @test + */ + public function checkAgainstDefaultValue() + { + $searchString = $this->searchPhrase->checkAgainstDefaultValue('Hallo'); + $this->assertEquals('hallo', $searchString); - - - public function setUp() { - $searchLib = $this->getMock('tx_kesearch_lib'); - $searchLib->extConf['searchWordLength'] = 4; - $searchLib->expects($this->any()) - ->method('pi_getLL') - ->will($this->returnValue('Bitte geben Sie einen Suchbegriff ein')); - - $this->searchPhrase = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib_searchphrase'); - - $this->searchPhrase->initialize($searchLib); - } - - public function tearDown() { - unset($this->searchPhrase); - } - - - - - - /** - * Test checkAgainstDefaultValue - * - * @test - */ - public function checkAgainstDefaultValue() { - $searchString = $this->searchPhrase->checkAgainstDefaultValue('Hallo'); - $this->assertEquals('hallo', $searchString); - - $searchString = $this->searchPhrase->checkAgainstDefaultValue('Bitte geben Sie einen Suchbegriff ein'); - $this->assertEquals('', $searchString); - } - - /** - * Test explodeSearchPhrase - * - * @test - */ - public function checkExplodeSearchPhrase() { - // the following samples are from MySQL: - // Link: http://dev.mysql.com/doc/refman/5.1/de/fulltext-boolean.html - - $matches = $this->searchPhrase->explodeSearchPhrase('apple banana'); - $shouldMatches = array( - 'apple', - 'banana' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('+apple +juice'); - $shouldMatches = array( - '+apple', - '+juice' - ); - $this->assertEquals($shouldMatches, $matches); - - - // +tag has 4 letters. But "tag" has only 3 letters, so it must be excluded - $matches = $this->searchPhrase->explodeSearchPhrase('+search +tag'); - $shouldMatches = array( - '+search' - ); - $this->assertEquals($shouldMatches, $matches); - - - // an machines which are not utf8 compatible "ü" will be converted to an 2 letter long value. - // so this method will return the value. But that's wrong! - // on machines which are configured right, this method will results in an empty array - $matches = $this->searchPhrase->explodeSearchPhrase('tür'); - $shouldMatches = array(); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('+apple macintosh'); - $shouldMatches = array( - '+apple', - 'macintosh' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('+apple -macintosh'); - $shouldMatches = array( - '+apple', - '-macintosh' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('+apple ~macintosh'); - $shouldMatches = array( - '+apple', - '~macintosh' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('apple*'); - $shouldMatches = array( - 'apple*' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('juice'); - $shouldMatches = array( - 'juice' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('"some words"'); - $shouldMatches = array( - '\"some words\"' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('+"find me" "and that"'); - $shouldMatches = array( - '+\"find me\"', - '\"and that\"' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('wie geht\'s'); - $shouldMatches = array( - 'geht\\\'s' - ); - $this->assertEquals($shouldMatches, $matches); - - - $matches = $this->searchPhrase->explodeSearchPhrase('"content elements" +"by several"'); - $shouldMatches = array( - '\"content elements\"', - '+\"by several\"' - ); - $this->assertEquals($shouldMatches, $matches); - } -} \ No newline at end of file + $searchString = $this->searchPhrase->checkAgainstDefaultValue('Bitte geben Sie einen Suchbegriff ein'); + $this->assertEquals('', $searchString); + } + + /** + * Test explodeSearchPhrase + * @test + */ + public function checkExplodeSearchPhrase() + { + // the following samples are from MySQL: + // Link: http://dev.mysql.com/doc/refman/5.1/de/fulltext-boolean.html + + $matches = $this->searchPhrase->explodeSearchPhrase('apple banana'); + $shouldMatches = array( + 'apple', + 'banana' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('+apple +juice'); + $shouldMatches = array( + '+apple', + '+juice' + ); + $this->assertEquals($shouldMatches, $matches); + + + // +tag has 4 letters. But "tag" has only 3 letters, so it must be excluded + $matches = $this->searchPhrase->explodeSearchPhrase('+search +tag'); + $shouldMatches = array( + '+search' + ); + $this->assertEquals($shouldMatches, $matches); + + + // an machines which are not utf8 compatible "ü" will be converted to an 2 letter long value. + // so this method will return the value. But that's wrong! + // on machines which are configured right, this method will results in an empty array + $matches = $this->searchPhrase->explodeSearchPhrase('tür'); + $shouldMatches = array(); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('+apple macintosh'); + $shouldMatches = array( + '+apple', + 'macintosh' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('+apple -macintosh'); + $shouldMatches = array( + '+apple', + '-macintosh' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('+apple ~macintosh'); + $shouldMatches = array( + '+apple', + '~macintosh' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('apple*'); + $shouldMatches = array( + 'apple*' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('juice'); + $shouldMatches = array( + 'juice' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('"some words"'); + $shouldMatches = array( + '\"some words\"' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('+"find me" "and that"'); + $shouldMatches = array( + '+\"find me\"', + '\"and that\"' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('wie geht\'s'); + $shouldMatches = array( + 'geht\\\'s' + ); + $this->assertEquals($shouldMatches, $matches); + + + $matches = $this->searchPhrase->explodeSearchPhrase('"content elements" +"by several"'); + $shouldMatches = array( + '\"content elements\"', + '+\"by several\"' + ); + $this->assertEquals($shouldMatches, $matches); + } +} diff --git a/Tests/lib/class.tx_kesearch_lib_searchresultTest.php b/Tests/lib/class.tx_kesearch_lib_searchresultTest.php index 50b9c389..d89f1728 100644 --- a/Tests/lib/class.tx_kesearch_lib_searchresultTest.php +++ b/Tests/lib/class.tx_kesearch_lib_searchresultTest.php @@ -1,143 +1,141 @@

    1. Überblick

    Die Bildung nimmt einen zentralen Platz in der öffentlichen Diskussion in Lettland ein. Nach der Unabhängigkeit begann eine umfangreiche Bildungsreform, die bis heute anhält. Eine der ausdrücklichen Prioritäten der lettischen Regierung ist die Integration des Landes in den europäischen Bildungsraum. Sie ist entschlossen, die Ziele von Lissabon und Kopenhagen zu erreichen. Auch die Mehrheit der Bevölkerung räumt der Bildung, insbesondere im beruflichen Zusammenhang, einen hohen Stellenwert ein. Viele Erwachsene absolvieren ein Zweitstudium, Lehrer bezahlen Fortbildungen aus eigener Tasche und junge Menschen holen in Abendschulen ihren Schulabschluss nach.
    Die Fragen der allgemeinen und der beruflichen Bildung sowie der Weiterbildung liegen in der Zuständigkeit des Ministeriums für Bildung und Wissenschaft (MBW), des Sozialministeriums und des Wirtschaftsministeriums.
    Für die Bereiche Vorschulerziehung, Primar-, Grund- und Basisschulen, Schulen für geistig Behinderte, Internate, Kinderheime und außerschulische Einrichtungen sind die Selbstverwaltungen (Städte und Gemeinden) verantwortlich. Sie überwachen die vom MBW vorgegebenen Standards. Darüber hinaus gibt es mehrere Fachgremien, die unter Aufsicht des MBW stehen. Eine institutionell herausragende Position nimmt für die berufliche Aus- und Weiterbildung das Berufsbildungszentrum (PIC) ein.
    Eine für den Hochschulbereich vergleichbare Funktion hat das Zentrum zur Bewertung der Qualität der Hochschulbildung. Die gesetzlichen Grundlagen für die allgemeine und berufliche Bildung sind im Bildungsgesetz (1998), im Berufsbildungsgesetz (1999) sowie im Hochschulgesetz (1999) formuliert.

    Bildungssystem

    Die lettische allgemeinbildende Schule umfasst zwölf Schuljahre. Für die neunjährige Grundbildung in der allgemeinen Schule besteht Schulpflicht. Sie ist unterteilt in eine vierjährige Primarstufe und eine fünfjährige untere Sekundarstufe. Daran kann sich ein dreijähriger erweiterter Bildungsgang in der oberen Sekundarstufe anschließen. Das Eintrittsalter in die allgemeine Schule liegt bei sieben Jahren.

    Es wird ein zehnstufiges Bewertungssystem verwendet, wobei die Noten 9 und 10 nur für herausragende Leistungen vergeben werden. Englisch wird ab der dritten Klasse unterrichtet, eine zweite Fremdsprache kommt in der sechsten Klasse hinzu. Mehr als die Hälfte aller Absolventen der allgemeinen Schule besucht die obere Sekundarstufe, wobei vier unterschiedliche Richtungen gewählt werden können:

    • ein allgemeines Profil ohne Schwerpunkt in einer bestimmten Fächergruppe;
    • ein naturwissenschaftlicher Schwerpunkt;
    • ein geisteswissenschaftlicher Schwerpunkt oder
    • ein berufsfeldbezogener Schwerpunkt.

    Pflichtfächer unabhängig vom Schwerpunkt sind: Lettische Sprache und Literatur, Mathematik, Geschichte, eine Fremdsprache, Sport, angewandte Informatik und Grundlagen der Wirtschaft. Hinzu kommen schwerpunktbezogene Pflichtfächer und Wahlfächer, die ca. 25 % des Unterrichts ausmachen. Für alle Unterrichtsfächer wird Grundkursniveau und Leistungskursniveau angeboten. Die Schüler müssen sich in mindestens einem Fach für das Leistungskursniveau entscheiden. Um das Zeugnis über den erfolgreichen Besuch der oberen Sekundarstufe zu erwerben, müssen fünf Abschlussprüfungen durchlaufen werden (Lettische Sprache und Literatur, ein jährlich wechselndes, zentral vorgegebenes Prüfungsfach und drei von den Schülern zu wählende Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe erworbene Hochschulreife wird in Deutschland uneingeschränkt als Studienzugangsberechtigung anerkannt.

    '; - - - - - - public function setUp() { - $GLOBALS['TSFE']->csConvObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('t3lib_cs'); - } - - public function tearDown() { - unset($GLOBALS['TSFE']->csConvObj); - } - - - - - - /** - * Test if the words in array are found in string - * - * @test - */ - public function checkIsArrayOfWordsInString() { - $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); - $sr = new tx_kesearch_lib_searchresult($lib); - - // check if one word can be found - $wordArray = array( - 'Abgangszeugnis' - ); - $result = $sr->isArrayOfWordsInString($wordArray, $this->content); - $this->assertEquals(TRUE, $result); - - // check if some of the words in the array can be found - // there are some valid words in this array - $wordArray = array( - 'Abgangszeugnis', - 'Schwerpunkt', - 'Wahlfächer', - 'Trallala' - ); - $result = $sr->isArrayOfWordsInString($wordArray, $this->content); - $this->assertEquals(TRUE, $result); - - // check if some of the words in the array can be found - // no word in this array is valid - $wordArray = array( - 'Abgangszaugnis', - 'Schwärpunkt', - 'Wahlfaecher', - 'Trallala' - ); - $result = $sr->isArrayOfWordsInString($wordArray, $this->content); - $this->assertEquals(FALSE, $result); - - // check if all of the words in the array can be found - $wordArray = array( - 'Abgangszeugnis', - 'Schwerpunkt', - 'Wahlfächer', - 'Trallala' - ); - $result = $sr->isArrayOfWordsInString($wordArray, $this->content, TRUE); - $this->assertEquals(FALSE, $result); - } - - /** - * Test if highlighting works correct - * - * @test - */ - public function checkHighlightArrayOfWordsInContent() { - $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); - - $lib->conf['resultChars'] = 300; - $lib->swords = array( - 'Abgangszeugnis' - ); - - $lib->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tslib_cObj'); - $sr = new tx_kesearch_lib_searchresult($lib); - - // highlight one word - $wordArray = array( - 'Abgangszeugnis' - ); - $result = $sr->highlightArrayOfWordsInContent($wordArray, $this->content); - $this->assertContains('', $result); - - // highlight one word - // but in this case the word doesn't exists - $wordArray = array( - 'Abgangszaugnis' - ); - $result = $sr->highlightArrayOfWordsInContent($wordArray, $this->content); - $this->assertNotContains('', $result); - - // highlight a word in a teaser - $wordArray = array( - 'Abgangszeugnis' - ); - $result = $sr->highlightArrayOfWordsInContent($wordArray, $sr->buildTeaserContent($this->content)); - $this->assertContains('', $result); - } - - /** - * Test if building the teaser works correct - * - * @test - */ - public function checkBuildTeaserContent() { - $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); - $lib->conf['resultChars'] = 300; - $lib->swords = array( - 'Abgangszeugnis' - ); - - $lib->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tslib_cObj'); - $sr = new tx_kesearch_lib_searchresult($lib); - - // test without highlighting - $result = $sr->buildTeaserContent($this->content); - $resultShouldBe = '... Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe...'; - // in some cases there are many spaces one after the other which makes this assertion fail. - // That's why I replace them with single spaces - $result = preg_replace('/[ ]+/', ' ', $result); - $resultShouldBe = preg_replace('/[ ]+/', ' ', $resultShouldBe); - $this->assertEquals($resultShouldBe, $result); - - // test with highlighting - $lib->conf['highlightSword'] = TRUE; - $result = $sr->buildTeaserContent($this->content); - $resultShouldBe = '... Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe...'; - // in some cases there are many spaces one after the other which makes this assertion fail. - // That's why I replace them with single spaces - $result = preg_replace('/[ ]+/', ' ', $result); - $resultShouldBe = preg_replace('/[ ]+/', ' ', $resultShouldBe); - $this->assertEquals($resultShouldBe, $result); - } -} \ No newline at end of file + +class SearchResultTest extends Tx_Extbase_BaseTestCase +{ + protected $content = '

    1. Überblick

    Die Bildung nimmt einen zentralen Platz in der öffentlichen Diskussion in Lettland ein. Nach der Unabhängigkeit begann eine umfangreiche Bildungsreform, die bis heute anhält. Eine der ausdrücklichen Prioritäten der lettischen Regierung ist die Integration des Landes in den europäischen Bildungsraum. Sie ist entschlossen, die Ziele von Lissabon und Kopenhagen zu erreichen. Auch die Mehrheit der Bevölkerung räumt der Bildung, insbesondere im beruflichen Zusammenhang, einen hohen Stellenwert ein. Viele Erwachsene absolvieren ein Zweitstudium, Lehrer bezahlen Fortbildungen aus eigener Tasche und junge Menschen holen in Abendschulen ihren Schulabschluss nach.
    Die Fragen der allgemeinen und der beruflichen Bildung sowie der Weiterbildung liegen in der Zuständigkeit des Ministeriums für Bildung und Wissenschaft (MBW), des Sozialministeriums und des Wirtschaftsministeriums.
    Für die Bereiche Vorschulerziehung, Primar-, Grund- und Basisschulen, Schulen für geistig Behinderte, Internate, Kinderheime und außerschulische Einrichtungen sind die Selbstverwaltungen (Städte und Gemeinden) verantwortlich. Sie überwachen die vom MBW vorgegebenen Standards. Darüber hinaus gibt es mehrere Fachgremien, die unter Aufsicht des MBW stehen. Eine institutionell herausragende Position nimmt für die berufliche Aus- und Weiterbildung das Berufsbildungszentrum (PIC) ein.
    Eine für den Hochschulbereich vergleichbare Funktion hat das Zentrum zur Bewertung der Qualität der Hochschulbildung. Die gesetzlichen Grundlagen für die allgemeine und berufliche Bildung sind im Bildungsgesetz (1998), im Berufsbildungsgesetz (1999) sowie im Hochschulgesetz (1999) formuliert.

    Bildungssystem

    Die lettische allgemeinbildende Schule umfasst zwölf Schuljahre. Für die neunjährige Grundbildung in der allgemeinen Schule besteht Schulpflicht. Sie ist unterteilt in eine vierjährige Primarstufe und eine fünfjährige untere Sekundarstufe. Daran kann sich ein dreijähriger erweiterter Bildungsgang in der oberen Sekundarstufe anschließen. Das Eintrittsalter in die allgemeine Schule liegt bei sieben Jahren.

    Es wird ein zehnstufiges Bewertungssystem verwendet, wobei die Noten 9 und 10 nur für herausragende Leistungen vergeben werden. Englisch wird ab der dritten Klasse unterrichtet, eine zweite Fremdsprache kommt in der sechsten Klasse hinzu. Mehr als die Hälfte aller Absolventen der allgemeinen Schule besucht die obere Sekundarstufe, wobei vier unterschiedliche Richtungen gewählt werden können:

    • ein allgemeines Profil ohne Schwerpunkt in einer bestimmten Fächergruppe;
    • ein naturwissenschaftlicher Schwerpunkt;
    • ein geisteswissenschaftlicher Schwerpunkt oder
    • ein berufsfeldbezogener Schwerpunkt.

    Pflichtfächer unabhängig vom Schwerpunkt sind: Lettische Sprache und Literatur, Mathematik, Geschichte, eine Fremdsprache, Sport, angewandte Informatik und Grundlagen der Wirtschaft. Hinzu kommen schwerpunktbezogene Pflichtfächer und Wahlfächer, die ca. 25 % des Unterrichts ausmachen. Für alle Unterrichtsfächer wird Grundkursniveau und Leistungskursniveau angeboten. Die Schüler müssen sich in mindestens einem Fach für das Leistungskursniveau entscheiden. Um das Zeugnis über den erfolgreichen Besuch der oberen Sekundarstufe zu erwerben, müssen fünf Abschlussprüfungen durchlaufen werden (Lettische Sprache und Literatur, ein jährlich wechselndes, zentral vorgegebenes Prüfungsfach und drei von den Schülern zu wählende Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe erworbene Hochschulreife wird in Deutschland uneingeschränkt als Studienzugangsberechtigung anerkannt.

    '; + + + public function setUp() + { + $GLOBALS['TSFE']->csConvObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('t3lib_cs'); + } + + public function tearDown() + { + unset($GLOBALS['TSFE']->csConvObj); + } + + + /** + * Test if the words in array are found in string + * @test + */ + public function checkIsArrayOfWordsInString() + { + $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); + $sr = new tx_kesearch_lib_searchresult($lib); + + // check if one word can be found + $wordArray = array( + 'Abgangszeugnis' + ); + $result = $sr->isArrayOfWordsInString($wordArray, $this->content); + $this->assertEquals(true, $result); + + // check if some of the words in the array can be found + // there are some valid words in this array + $wordArray = array( + 'Abgangszeugnis', + 'Schwerpunkt', + 'Wahlfächer', + 'Trallala' + ); + $result = $sr->isArrayOfWordsInString($wordArray, $this->content); + $this->assertEquals(true, $result); + + // check if some of the words in the array can be found + // no word in this array is valid + $wordArray = array( + 'Abgangszaugnis', + 'Schwärpunkt', + 'Wahlfaecher', + 'Trallala' + ); + $result = $sr->isArrayOfWordsInString($wordArray, $this->content); + $this->assertEquals(false, $result); + + // check if all of the words in the array can be found + $wordArray = array( + 'Abgangszeugnis', + 'Schwerpunkt', + 'Wahlfächer', + 'Trallala' + ); + $result = $sr->isArrayOfWordsInString($wordArray, $this->content, true); + $this->assertEquals(false, $result); + } + + /** + * Test if highlighting works correct + * @test + */ + public function checkHighlightArrayOfWordsInContent() + { + $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); + + $lib->conf['resultChars'] = 300; + $lib->swords = array( + 'Abgangszeugnis' + ); + + $lib->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tslib_cObj'); + $sr = new tx_kesearch_lib_searchresult($lib); + + // highlight one word + $wordArray = array( + 'Abgangszeugnis' + ); + $result = $sr->highlightArrayOfWordsInContent($wordArray, $this->content); + $this->assertContains('', $result); + + // highlight one word + // but in this case the word doesn't exists + $wordArray = array( + 'Abgangszaugnis' + ); + $result = $sr->highlightArrayOfWordsInContent($wordArray, $this->content); + $this->assertNotContains('', $result); + + // highlight a word in a teaser + $wordArray = array( + 'Abgangszeugnis' + ); + $result = $sr->highlightArrayOfWordsInContent($wordArray, $sr->buildTeaserContent($this->content)); + $this->assertContains('', $result); + } + + /** + * Test if building the teaser works correct + * @test + */ + public function checkBuildTeaserContent() + { + $lib = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_lib'); + $lib->conf['resultChars'] = 300; + $lib->swords = array( + 'Abgangszeugnis' + ); + + $lib->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tslib_cObj'); + $sr = new tx_kesearch_lib_searchresult($lib); + + // test without highlighting + $result = $sr->buildTeaserContent($this->content); + $resultShouldBe = '... Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe...'; + // in some cases there are many spaces one after the other which makes this assertion fail. + // That's why I replace them with single spaces + $result = preg_replace('/[ ]+/', ' ', $result); + $resultShouldBe = preg_replace('/[ ]+/', ' ', $resultShouldBe); + $this->assertEquals($resultShouldBe, $result); + + // test with highlighting + $lib->conf['highlightSword'] = true; + $result = $sr->buildTeaserContent($this->content); + $resultShouldBe = '... Fächer). Es werden zentral einheitliche Prüfungen durchgeführt. Bei Zensuren in wenigstens zwölf Fächern nicht unter der Note 4 berechtigt das Abgangszeugnis der zwölften Klasse zum Studium an Universitäten und Hochschulen. Die in Lettland seit 1999 an der allgemeinbildenden oberen Sekundarstufe...'; + // in some cases there are many spaces one after the other which makes this assertion fail. + // That's why I replace them with single spaces + $result = preg_replace('/[ ]+/', ' ', $result); + $resultShouldBe = preg_replace('/[ ]+/', ' ', $resultShouldBe); + $this->assertEquals($resultShouldBe, $result); + } +} diff --git a/cli/class.cli_kesearch.php b/cli/class.cli_kesearch.php index b747fffe..d717e830 100644 --- a/cli/class.cli_kesearch.php +++ b/cli/class.cli_kesearch.php @@ -1,39 +1,37 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ - -if (!defined('TYPO3_cliMode')) die('You cannot run this script directly!'); - -class tx_kesearch_cli extends \TYPO3\CMS\Core\Controller\CommandLineController { + * Copyright notice + * (c) 2010 Andreas Kiefer + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +if (!defined('TYPO3_cliMode')) { + die('You cannot run this script directly!'); +} - /** - * Constructor - */ - function tx_kesearch_cli () { +class tx_kesearch_cli extends \TYPO3\CMS\Core\Controller\CommandLineController +{ - // Running parent class constructor - parent::__construct(); + /** + * Constructor + */ + function tx_kesearch_cli() + { + // Running parent class constructor + parent::__construct(); // Setting help texts: $this->cli_help['name'] = 'ke_search Command Line Interface'; @@ -45,46 +43,43 @@ function tx_kesearch_cli () { /** * CLI engine - * * @param array Command line arguments * @return string */ - function cli_main($argv) { + function cli_main($argv) + { - // make instance of indexer + // make instance of indexer // get task (function) $task = (string)$this->cli_args['_DEFAULT'][1]; - // get extension configuration - $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); - - // switch between tasks - switch ($task) { - - default: - $this->cli_validateArgs(); - $this->cli_help(); - break; - - case 'startIndexing': - - $indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); - $this->cli_echo(chr(10)); - $verboseMode = true; - $cleanup = $this->extConf['cleanupInterval']; - $response = $indexer->startIndexing($verboseMode, $this->extConf, 'CLI'); - $response = str_replace('

    ', chr(10),$response); - $response = str_replace('
    ', chr(10),$response); - $response = strip_tags($response); - $this->cli_echo($response.chr(10).chr(10)); - break; - } - + // get extension configuration + $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ke_search']); + + // switch between tasks + switch ($task) { + default: + $this->cli_validateArgs(); + $this->cli_help(); + break; + + case 'startIndexing': + $indexer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_indexer'); + $this->cli_echo(chr(10)); + $verboseMode = true; + $cleanup = $this->extConf['cleanupInterval']; + $response = $indexer->startIndexing($verboseMode, $this->extConf, 'CLI'); + $response = str_replace('

    ', chr(10), $response); + $response = str_replace('
    ', chr(10), $response); + $response = strip_tags($response); + $this->cli_echo($response . chr(10) . chr(10)); + break; + } } } // Call the functionality -$cleanerObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_cli'); -$cleanerObj->cli_main($_SERVER['argv']); \ No newline at end of file +$cleanerObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_kesearch_cli'); +$cleanerObj->cli_main($_SERVER['argv']); diff --git a/ext_emconf.php b/ext_emconf.php index 81b66c9d..76fba847 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,40 +1,37 @@ 'Faceted Search', - 'description' => 'Faceted fulltext search for TYPO3. Fast, flexible and easy to use. Very easy to install. Fast (tested with over 200.000 records) and flexible (you can write your own indexers). Indexes content directly from the databases. Visit kesearch.de for further information and documentation.', - 'category' => 'plugin', - 'shy' => 0, - 'version' => '2.4.1', - 'dependencies' => '', - 'conflicts' => '', - 'priority' => '', - 'loadOrder' => '', - 'module' => '', - 'state' => 'stable', - 'uploadfolder' => 0, - 'createDirs' => '', - 'modify_tables' => '', - 'clearcacheonload' => 0, - 'lockType' => '', - 'author' => 'Christian Buelter (team.inmedias)', - 'author_email' => 'christian.buelter@inmedias.de', - 'author_company' => 'team.inmedias', - 'CGLcompliance' => '', - 'CGLcompliance_note' => '', - 'constraints' => array( - 'depends' => array( - 'php' => '5.5.0-7.1.99', - 'typo3' => '7.6.0-7.99.99', - ), - 'conflicts' => array( - ), - 'suggests' => array( - ), - ), - 'suggests' => array( - ), - 'autoload' => array( - 'psr-4' => array('TeaminmediasPluswerk\\KeSearch\\' => 'Classes'), - 'classmap' => array('Classes', 'cli', 'pi1', 'pi2', 'pi3'), - ), + 'title' => 'Faceted Search', + 'description' => 'Faceted fulltext search for TYPO3. Fast, flexible and easy to use. Very easy to install. Fast (tested with over 200.000 records) and flexible (you can write your own indexers). Indexes content directly from the databases. Visit kesearch.de for further information and documentation.', + 'category' => 'plugin', + 'shy' => 0, + 'version' => '2.4.1', + 'dependencies' => '', + 'conflicts' => '', + 'priority' => '', + 'loadOrder' => '', + 'module' => '', + 'state' => 'stable', + 'uploadfolder' => 0, + 'createDirs' => '', + 'modify_tables' => '', + 'clearcacheonload' => 0, + 'lockType' => '', + 'author' => 'Christian Buelter (team.inmedias)', + 'author_email' => 'christian.buelter@inmedias.de', + 'author_company' => 'team.inmedias', + 'CGLcompliance' => '', + 'CGLcompliance_note' => '', + 'constraints' => array( + 'depends' => array( + 'php' => '5.5.0-7.1.99', + 'typo3' => '7.6.0-7.99.99', + ), + 'conflicts' => array(), + 'suggests' => array(), + ), + 'suggests' => array(), + 'autoload' => array( + 'psr-4' => array('TeaminmediasPluswerk\\KeSearch\\' => 'Classes'), + 'classmap' => array('Classes', 'cli', 'pi1', 'pi2', 'pi3'), + ), ); diff --git a/ext_localconf.php b/ext_localconf.php index 120102ce..aa1ea4ff 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,30 +1,67 @@ '); -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43($_EXTKEY, 'pi1/class.tx_kesearch_pi1.php', '_pi1', 'list_type', 0); -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43($_EXTKEY, 'pi2/class.tx_kesearch_pi2.php', '_pi2', 'list_type', 0); -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43($_EXTKEY, 'pi3/class.tx_kesearch_pi3.php', '_pi3', 'list_type', 0); +include_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY) + . '/Classes/Backend/class.user_filterlist.php'); + +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig( + '' +); + +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43( + $_EXTKEY, + 'pi1/class.tx_kesearch_pi1.php', + '_pi1', + 'list_type', + 0 +); + +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43( + $_EXTKEY, + 'pi2/class.tx_kesearch_pi2.php', + '_pi2', + 'list_type', + 0 +); + +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43( + $_EXTKEY, + 'pi3/class.tx_kesearch_pi3.php', + '_pi3', + 'list_type', + 0 +); // use hooks for generation of sortdate values -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['registerAdditionalFields'][] = 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyPagesIndexEntry'][] = 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyYACIndexEntry'][] = 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyContentIndexEntry'][] = 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTemplaVoilaIndexEntry'][] = 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['registerAdditionalFields'][] = + 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyPagesIndexEntry'][] = + 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyYACIndexEntry'][] = + 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyContentIndexEntry'][] = + 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; + +$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyTemplaVoilaIndexEntry'][] = + 'EXT:ke_search/Classes/hooks/class.user_kesearchhooks.php:user_kesearch_sortdate'; // add scheduler task $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_kesearch_indexertask'] = array( - 'extension' => $_EXTKEY, - 'title' => 'Indexing process for ke_search', - 'description' => 'This task updates the ke_search index' -); \ No newline at end of file + 'extension' => $_EXTKEY, + 'title' => 'Indexing process for ke_search', + 'description' => 'This task updates the ke_search index' +); diff --git a/ext_tables.php b/ext_tables.php index 7db56b59..b42b5f64 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -1,18 +1,21 @@ array( - 'exclude' => 1, - 'label' => 'LLL:EXT:ke_search/locallang_db.xml:pages.tx_kesearch_tags', - 'config' => array( - 'type' => 'select', - 'renderType' => 'selectSingleBox', - 'size' => 10, - 'minitems' => 0, - 'maxitems' => 100, - 'items' => array(), - 'allowNonIdValues' => true, - 'itemsProcFunc' => 'user_filterlist->getListOfAvailableFiltersForTCA', - ) - ), + 'tx_kesearch_tags' => array( + 'exclude' => 1, + 'label' => 'LLL:EXT:ke_search/locallang_db.xml:pages.tx_kesearch_tags', + 'config' => array( + 'type' => 'select', + 'renderType' => 'selectSingleBox', + 'size' => 10, + 'minitems' => 0, + 'maxitems' => 100, + 'items' => array(), + 'allowNonIdValues' => true, + 'itemsProcFunc' => 'user_filterlist->getListOfAvailableFiltersForTCA', + ) + ), ); TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('pages', $tempColumns); TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('pages', 'tx_kesearch_tags;;;;1-1-1'); // add module if (TYPO3_MODE == 'BE') { - \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( - 'TeaminmediasPluswerk.' . $_EXTKEY, - 'web', // Main area - 'backend_module', // Name of the module - '', // Position of the module - array( // Allowed controller action combinations - 'BackendModule' => 'startIndexing,indexedContent,indexTableInformation,searchwordStatistics,clearSearchIndex,lastIndexingReport,alert', - ), - array( // Additional configuration - 'access' => 'user,group', - 'icon' => 'EXT:ke_search/Resources/Public/Icons/moduleicon.gif', - 'labels' => 'LLL:EXT:ke_search/Resources/Private/Language/locallang_mod.xml', - ) - ); + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( + 'TeaminmediasPluswerk.' . $_EXTKEY, + 'web', // Main area + 'backend_module', // Name of the module + '', // Position of the module + array( // Allowed controller action combinations + 'BackendModule' => 'startIndexing,indexedContent,indexTableInformation,searchwordStatistics,clearSearchIndex,lastIndexingReport,alert', + ), + array( // Additional configuration + 'access' => 'user,group', + 'icon' => 'EXT:ke_search/Resources/Public/Icons/moduleicon.gif', + 'labels' => 'LLL:EXT:ke_search/Resources/Private/Language/locallang_mod.xml', + ) + ); } - // add plugins -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(array( - 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi1', - $_EXTKEY . '_pi1', - $extRelPath . 'ext_icon.gif'), 'list_type' +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin( + array( + 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi1', + $_EXTKEY . '_pi1', + $extRelPath . 'ext_icon.gif' + ), + 'list_type' ); -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(array( - 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi2', - $_EXTKEY . '_pi2', - $extRelPath . 'ext_icon.gif'), 'list_type' +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin( + array( + 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi2', + $_EXTKEY . '_pi2', + $extRelPath . 'ext_icon.gif' + ), + 'list_type' ); -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(array( - 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi3', - $_EXTKEY . '_pi3', - $extRelPath . 'ext_icon.gif'), 'list_type' +TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin( + array( + 'LLL:EXT:ke_search/locallang_db.xml:tt_content.list_type_pi3', + $_EXTKEY . '_pi3', + $extRelPath . 'ext_icon.gif' + ), + 'list_type' ); // class for displaying the category tree for tt_news in BE forms. if (TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('tt_news')) { - include_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('tt_news') . 'lib/class.tx_ttnews_TCAform_selectTree.php'); + include_once(TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('tt_news') + . 'lib/class.tx_ttnews_TCAform_selectTree.php'); } if (TYPO3_MODE == 'BE') { - $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi1_wizicon'] = $extPath . 'pi1/class.tx_kesearch_pi1_wizicon.php'; - $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi2_wizicon'] = $extPath . 'pi2/class.tx_kesearch_pi2_wizicon.php'; - $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi3_wizicon'] = $extPath . 'pi3/class.tx_kesearch_pi3_wizicon.php'; -} \ No newline at end of file + $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi1_wizicon'] = + $extPath . 'pi1/class.tx_kesearch_pi1_wizicon.php'; + + $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi2_wizicon'] = + $extPath . 'pi2/class.tx_kesearch_pi2_wizicon.php'; + + $TBE_MODULES_EXT['xMOD_db_new_content_el']['addElClasses']['tx_kesearch_pi3_wizicon'] = + $extPath . 'pi3/class.tx_kesearch_pi3_wizicon.php'; +} diff --git a/pi1/class.tx_kesearch_pi1.php b/pi1/class.tx_kesearch_pi1.php index 85aa29a7..94fa9443 100644 --- a/pi1/class.tx_kesearch_pi1.php +++ b/pi1/class.tx_kesearch_pi1.php @@ -1,100 +1,96 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ + * Copyright notice + * (c) 2010 Andreas Kiefer + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ use \TYPO3\CMS\Core\Utility\GeneralUtility; /** * Plugin 'Faceted search - searchbox and filters' for the 'ke_search' extension. - * - * @author Andreas Kiefer - * @author Christian Bülter - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @author Christian Bülter + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi1 extends tx_kesearch_lib { +class tx_kesearch_pi1 extends tx_kesearch_lib +{ /** * @var \TYPO3\CMS\Fluid\View\StandaloneView */ protected $searchFormView; - // Path to this script relative to the extension dir. - var $scriptRelPath = 'pi1/class.tx_kesearch_pi1.php'; + // Path to this script relative to the extension dir. + public $scriptRelPath = 'pi1/class.tx_kesearch_pi1.php'; - /** - * The main method of the PlugIn - * - * @param string $content: The PlugIn content - * @param array $conf: The PlugIn configuration - * @return string The content that is displayed on the website - */ - function main($content, $conf) { - $this->ms = GeneralUtility::milliseconds(); - $this->conf = $conf; - $this->pi_setPiVarDefaults(); - $this->pi_loadLL(); + /** + * The main method of the PlugIn + * @param string $content : The PlugIn content + * @param array $conf : The PlugIn configuration + * @return string The content that is displayed on the website + */ + public function main($content, $conf) + { + $this->ms = GeneralUtility::milliseconds(); + $this->conf = $conf; + $this->pi_setPiVarDefaults(); + $this->pi_loadLL(); - // Configuring so caching is not expected. This value means that no cHash params are ever set. - // We do this, because it's a USER_INT object! - $this->pi_USER_INT_obj = 1; + // Configuring so caching is not expected. This value means that no cHash params are ever set. + // We do this, because it's a USER_INT object! + $this->pi_USER_INT_obj = 1; - // initializes plugin configuration - $this->init(); + // initializes plugin configuration + $this->init(); - // init template for pi1 - $this->initFluidTemplate(); + // init template for pi1 + $this->initFluidTemplate(); - // hook for initials - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->addInitials($this); - } - } + // hook for initials + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->addInitials($this); + } + } - // get content for searchbox - $this->getSearchboxContent(); + // get content for searchbox + $this->getSearchboxContent(); - // assign variables and do the rendering - $this->searchFormView->assignMultiple($this->fluidTemplateVariables); - $htmlOutput = $this->searchFormView->render(); + // assign variables and do the rendering + $this->searchFormView->assignMultiple($this->fluidTemplateVariables); + $htmlOutput = $this->searchFormView->render(); - return $htmlOutput; - } + return $htmlOutput; + } - /** - * inits the standalone fluid template - */ - public function initFluidTemplate() { - $this->searchFormView = GeneralUtility::makeInstance('TYPO3\\CMS\\Fluid\\View\\StandaloneView'); - $this->searchFormView->setPartialRootPaths([$this->conf['partialRootPath']]); - $this->searchFormView->setLayoutRootPaths([$this->conf['layoutRootPath']]); - $this->searchFormView->setTemplatePathAndFilename($this->conf['templateRootPath'] . 'SearchForm.html'); + /** + * inits the standalone fluid template + */ + public function initFluidTemplate() + { + $this->searchFormView = GeneralUtility::makeInstance('TYPO3\\CMS\\Fluid\\View\\StandaloneView'); + $this->searchFormView->setPartialRootPaths([$this->conf['partialRootPath']]); + $this->searchFormView->setLayoutRootPaths([$this->conf['layoutRootPath']]); + $this->searchFormView->setTemplatePathAndFilename($this->conf['templateRootPath'] . 'SearchForm.html'); - // make settings available in fluid template - $this->searchFormView->assign('conf', $this->conf); - $this->searchFormView->assign('extConf', $this->extConf); - $this->searchFormView->assign('extConfPremium', $this->extConfPremium); - } + // make settings available in fluid template + $this->searchFormView->assign('conf', $this->conf); + $this->searchFormView->assign('extConf', $this->extConf); + $this->searchFormView->assign('extConfPremium', $this->extConfPremium); + } } diff --git a/pi1/class.tx_kesearch_pi1_wizicon.php b/pi1/class.tx_kesearch_pi1_wizicon.php index ac7bf6e6..a311740e 100644 --- a/pi1/class.tx_kesearch_pi1_wizicon.php +++ b/pi1/class.tx_kesearch_pi1_wizicon.php @@ -23,48 +23,52 @@ * This copyright notice MUST APPEAR in all copies of the script! * ************************************************************* */ +use \TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + /** * Class that adds the wizard icon. - * - * @author Andreas Kiefer - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi1_wizicon { - - /** - * Processing the wizard items array - * - * @param array $wizardItems: The wizard items - * @return Modified array with wizard items - */ - function proc($wizardItems) { - global $LANG; +class tx_kesearch_pi1_wizicon +{ - $LL = $this->includeLocalLang(); - $extRelPath = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('ke_search'); + /** + * Processing the wizard items array + * + * @param array $wizardItems : The wizard items + * @return Modified array with wizard items + */ + public function proc($wizardItems) + { + global $LANG; - $wizardItems['plugins_tx_kesearch_pi1'] = array( - 'icon' => $extRelPath . 'pi1/ce_wiz.gif', - 'title' => $LANG->getLLL('pi_title', $LL), - 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), - 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi1' - ); + $LL = $this->includeLocalLang(); + $extRelPath = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('ke_search'); - return $wizardItems; - } + $wizardItems['plugins_tx_kesearch_pi1'] = array( + 'icon' => $extRelPath . 'pi1/ce_wiz.gif', + 'title' => $LANG->getLLL('pi_title', $LL), + 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), + 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi1' + ); - /** - * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. - * - * @return The array with language labels - */ - function includeLocalLang() { - $llFile = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search') . 'pi1/locallang.xml'; + return $wizardItems; + } - $xmlParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); - $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); - return $LOCAL_LANG; - } + /** + * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. + * + * @return The array with language labels + */ + public function includeLocalLang() + { + $llFile = ExtensionManagementUtility::extPath('ke_search') . 'pi1/locallang.xml'; + $xmlParser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); + $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); + return $LOCAL_LANG; + } -} \ No newline at end of file +} diff --git a/pi2/class.tx_kesearch_pi2.php b/pi2/class.tx_kesearch_pi2.php index 96262ac9..90dc880f 100644 --- a/pi2/class.tx_kesearch_pi2.php +++ b/pi2/class.tx_kesearch_pi2.php @@ -1,138 +1,134 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ + * Copyright notice + * (c) 2010 Andreas Kiefer (kennziffer.com) + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Plugin 'Faceted search - searchbox and filters' for the 'ke_search' extension. - * - * @author Andreas Kiefer - * @author Christian Bülter - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @author Christian Bülter + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi2 extends tx_kesearch_lib { +class tx_kesearch_pi2 extends tx_kesearch_lib +{ /** * @var \TYPO3\CMS\Fluid\View\StandaloneView */ protected $resultListView; - // Path to this script relative to the extension dir. - var $scriptRelPath = 'pi2/class.tx_kesearch_pi2.php'; - - /** - * The main method of the PlugIn - * - * @param string $content: The PlugIn content - * @param array $conf: The PlugIn configuration - * @return string The content that is displayed on the website - */ - function main($content, $conf) { - $this->ms = GeneralUtility::milliseconds(); - $this->conf = $conf; - $this->pi_setPiVarDefaults(); - - // use pi1 locallang values, since all the frontend locallang values for - // pi1, pi2 and pi3 are set in pi1 language file - $this->pi_loadLL('EXT:ke_search/pi1/locallang.xml'); - - // Configuring so caching is not expected. This value means that no cHash params are ever set. - // We do this, because it's a USER_INT object! - $this->pi_USER_INT_obj = 1; - - // initializes plugin configuration - $this->init(); - - if($this->conf['resultPage'] != $GLOBALS['TSFE']->id) { - $content = '
    ' . $this->pi_getLL('error_resultPage') . '
    '; - return $this->pi_wrapInBaseClass($content); - } - - // init template - $this->initFluidTemplate(); - - // hook for initials - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->addInitials($this); - } - } - - // assign isEmptySearch to fluid templates - $this->fluidTemplateVariables['isEmptySearch'] = $this->isEmptySearch; - - // render "no results"-message, "too short words"-message and finally the result list - $this->getSearchResults(); - - // number of results - $this->fluidTemplateVariables['numberofresults'] = $this->numberOfResults; - - // render links for sorting, fluid template variables are filled in class tx_kesearch_lib_sorting - $this->renderOrdering(); - - // process query time - $queryTime = (GeneralUtility::milliseconds() - $GLOBALS['TSFE']->register['ke_search_queryStartTime']); - $this->fluidTemplateVariables['queryTime'] = $queryTime; - $this->fluidTemplateVariables['queryTimeText'] = sprintf($this->pi_getLL('query_time'), $queryTime); - - // render pagebrowser - if ($GLOBALS['TSFE']->id == $this->conf['resultPage']) { - if ($this->conf['pagebrowserOnTop'] || $this->conf['pagebrowserAtBottom']) { - $this->renderPagebrowser(); - } - } - - // hook: modifyResultList - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyResultList'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyResultList'] as $_classRef) { - $_procObj = & GeneralUtility::getUserObj($_classRef); - $_procObj->modifyResultList($this->fluidTemplateVariables, $this); - } - } - - // generate HTML output - $this->resultListView->assignMultiple($this->fluidTemplateVariables); - $htmlOutput = $this->resultListView->render(); - - return $htmlOutput; - } - - /** - * inits the standalone fluid template - */ - public function initFluidTemplate() { - $this->resultListView = GeneralUtility::makeInstance('TYPO3\\CMS\\Fluid\\View\\StandaloneView'); - $this->resultListView->setPartialRootPaths([$this->conf['partialRootPath']]); - $this->resultListView->setLayoutRootPaths([$this->conf['layoutRootPath']]); - $this->resultListView->setTemplatePathAndFilename($this->conf['templateRootPath'] . 'ResultList.html'); - - // make settings available in fluid template - $this->resultListView->assign('conf', $this->conf); - $this->resultListView->assign('extConf', $this->extConf); - $this->resultListView->assign('extConfPremium', $this->extConfPremium); - } + // Path to this script relative to the extension dir. + public $scriptRelPath = 'pi2/class.tx_kesearch_pi2.php'; + + /** + * The main method of the PlugIn + * @param string $content : The PlugIn content + * @param array $conf : The PlugIn configuration + * @return string The content that is displayed on the website + */ + public function main($content, $conf) + { + $this->ms = GeneralUtility::milliseconds(); + $this->conf = $conf; + $this->pi_setPiVarDefaults(); + + // use pi1 locallang values, since all the frontend locallang values for + // pi1, pi2 and pi3 are set in pi1 language file + $this->pi_loadLL('EXT:ke_search/pi1/locallang.xml'); + + // Configuring so caching is not expected. This value means that no cHash params are ever set. + // We do this, because it's a USER_INT object! + $this->pi_USER_INT_obj = 1; + + // initializes plugin configuration + $this->init(); + + if ($this->conf['resultPage'] != $GLOBALS['TSFE']->id) { + $content = '
    ' . $this->pi_getLL('error_resultPage') . '
    '; + return $this->pi_wrapInBaseClass($content); + } + + // init template + $this->initFluidTemplate(); + + // hook for initials + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->addInitials($this); + } + } + + // assign isEmptySearch to fluid templates + $this->fluidTemplateVariables['isEmptySearch'] = $this->isEmptySearch; + + // render "no results"-message, "too short words"-message and finally the result list + $this->getSearchResults(); + + // number of results + $this->fluidTemplateVariables['numberofresults'] = $this->numberOfResults; + + // render links for sorting, fluid template variables are filled in class tx_kesearch_lib_sorting + $this->renderOrdering(); + + // process query time + $queryTime = (GeneralUtility::milliseconds() - $GLOBALS['TSFE']->register['ke_search_queryStartTime']); + $this->fluidTemplateVariables['queryTime'] = $queryTime; + $this->fluidTemplateVariables['queryTimeText'] = sprintf($this->pi_getLL('query_time'), $queryTime); + + // render pagebrowser + if ($GLOBALS['TSFE']->id == $this->conf['resultPage']) { + if ($this->conf['pagebrowserOnTop'] || $this->conf['pagebrowserAtBottom']) { + $this->renderPagebrowser(); + } + } + + // hook: modifyResultList + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyResultList'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyResultList'] as $_classRef) { + $_procObj = &GeneralUtility::getUserObj($_classRef); + $_procObj->modifyResultList($this->fluidTemplateVariables, $this); + } + } + + // generate HTML output + $this->resultListView->assignMultiple($this->fluidTemplateVariables); + $htmlOutput = $this->resultListView->render(); + + return $htmlOutput; + } + + /** + * inits the standalone fluid template + */ + public function initFluidTemplate() + { + $this->resultListView = GeneralUtility::makeInstance('TYPO3\\CMS\\Fluid\\View\\StandaloneView'); + $this->resultListView->setPartialRootPaths([$this->conf['partialRootPath']]); + $this->resultListView->setLayoutRootPaths([$this->conf['layoutRootPath']]); + $this->resultListView->setTemplatePathAndFilename($this->conf['templateRootPath'] . 'ResultList.html'); + + // make settings available in fluid template + $this->resultListView->assign('conf', $this->conf); + $this->resultListView->assign('extConf', $this->extConf); + $this->resultListView->assign('extConfPremium', $this->extConfPremium); + } } diff --git a/pi2/class.tx_kesearch_pi2_wizicon.php b/pi2/class.tx_kesearch_pi2_wizicon.php index 3abe8cce..8c7cbb18 100644 --- a/pi2/class.tx_kesearch_pi2_wizicon.php +++ b/pi2/class.tx_kesearch_pi2_wizicon.php @@ -23,48 +23,48 @@ * This copyright notice MUST APPEAR in all copies of the script! * ************************************************************* */ +use \TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + /** * Class that adds the wizard icon. - * - * @author Andreas Kiefer - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi2_wizicon { - - /** - * Processing the wizard items array - * - * @param array $wizardItems: The wizard items - * @return Modified array with wizard items - */ - function proc($wizardItems) { - global $LANG; - - $LL = $this->includeLocalLang(); - - $extRelPath = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('ke_search'); - - $wizardItems['plugins_tx_kesearch_pi2'] = array( - 'icon' => $extRelPath . 'pi2/ce_wiz.gif', - 'title' => $LANG->getLLL('pi_title', $LL), - 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), - 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi2' - ); - - return $wizardItems; - } +class tx_kesearch_pi2_wizicon +{ + /** + * Processing the wizard items array + * + * @param array $wizardItems : The wizard items + * @return Modified array with wizard items + */ + public function proc($wizardItems) + { + global $LANG; + $LL = $this->includeLocalLang(); + $extRelPath = ExtensionManagementUtility::extRelPath('ke_search'); + $wizardItems['plugins_tx_kesearch_pi2'] = array( + 'icon' => $extRelPath . 'pi2/ce_wiz.gif', + 'title' => $LANG->getLLL('pi_title', $LL), + 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), + 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi2' + ); - /** - * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. - * - * @return The array with language labels - */ - function includeLocalLang() { - $llFile = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search') . 'pi2/locallang.xml'; - $xmlParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); - $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); - return $LOCAL_LANG; - } + return $wizardItems; + } -} \ No newline at end of file + /** + * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. + * + * @return The array with language labels + */ + public function includeLocalLang() + { + $llFile = ExtensionManagementUtility::extPath('ke_search') . 'pi2/locallang.xml'; + $xmlParser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); + $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); + return $LOCAL_LANG; + } +} diff --git a/pi3/class.tx_kesearch_pi3.php b/pi3/class.tx_kesearch_pi3.php index 4dcecd8a..c05a0a73 100644 --- a/pi3/class.tx_kesearch_pi3.php +++ b/pi3/class.tx_kesearch_pi3.php @@ -1,171 +1,183 @@ -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* -* This script is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ + * Copyright notice + * (c) 2010 Andreas Kiefer (kennziffer.com) + * All rights reserved + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ /** * Plugin 'Faceted search - searchbox and filters' for the 'ke_search' extension. - * - * @author Andreas Kiefer (kennziffer.com) - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer (kennziffer.com) + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi3 extends tx_kesearch_lib { +class tx_kesearch_pi3 extends tx_kesearch_lib +{ - // Path to this script relative to the extension dir. - var $scriptRelPath = 'pi3/class.tx_kesearch_pi3.php'; + // Path to this script relative to the extension dir. + public $scriptRelPath = 'pi3/class.tx_kesearch_pi3.php'; - /** - * The main method of the PlugIn - * - * @param string $content: The PlugIn content - * @param array $conf: The PlugIn configuration - * @return The content that is displayed on the website - */ - function main($content, $conf) { + /** + * The main method of the PlugIn + * @param string $content : The PlugIn content + * @param array $conf : The PlugIn configuration + * @return string The content that is displayed on the website + */ + public function main($content, $conf) + { - $this->ms = TYPO3\CMS\Core\Utility\GeneralUtility::milliseconds(); - $this->conf = $conf; - $this->pi_setPiVarDefaults(); + $this->ms = TYPO3\CMS\Core\Utility\GeneralUtility::milliseconds(); + $this->conf = $conf; + $this->pi_setPiVarDefaults(); - // use pi1 locallang values, since all the frontend locallang values for - // pi1, pi2 and pi3 are set in pi1 language file - $this->pi_loadLL('EXT:ke_search/pi1/locallang.xml'); + // use pi1 locallang values, since all the frontend locallang values for + // pi1, pi2 and pi3 are set in pi1 language file + $this->pi_loadLL('EXT:ke_search/pi1/locallang.xml'); - // Configuring so caching is not expected. This value means that no cHash params are ever set. - // We do this, because it's a USER_INT object! - $this->pi_USER_INT_obj = 1; + // Configuring so caching is not expected. This value means that no cHash params are ever set. + // We do this, because it's a USER_INT object! + $this->pi_USER_INT_obj = 1; - // initializes plugin configuration - $this->init(); + // initializes plugin configuration + $this->init(); - // hook for initials - if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $_procObj->addInitials($this); - } - } + // hook for initials + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['initials'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $_procObj->addInitials($this); + } + } - // get templates - $template['multiselect'] = $this->cObj->getSubpart($this->templateCode, '###SUB_FILTER_MULTISELECT###'); - $template['multihidden'] = $this->cObj->getSubpart($template['multiselect'], '###SUB_FILTER_MULTISELECT_HIDDEN###'); - $template['multifilter'] = $this->cObj->getSubpart($template['multiselect'], '###SUB_FILTER_MULTISELECT_FILTER###'); - $template['multioption'] = $this->cObj->getSubpart($template['multifilter'], '###SUB_FILTER_MULTISELECT_OPTION###'); + // get templates + $template['multiselect'] = $this->cObj->getSubpart($this->templateCode, '###SUB_FILTER_MULTISELECT###'); + $template['multihidden'] = $this->cObj->getSubpart( + $template['multiselect'], + '###SUB_FILTER_MULTISELECT_HIDDEN###' + ); + $template['multifilter'] = $this->cObj->getSubpart( + $template['multiselect'], + '###SUB_FILTER_MULTISELECT_FILTER###' + ); + $template['multioption'] = $this->cObj->getSubpart( + $template['multifilter'], + '###SUB_FILTER_MULTISELECT_OPTION###' + ); - // get current filter - $filters = $this->filters->getFilters(); - foreach($filters as $filter) { - if($filter['target_pid'] == intval($GLOBALS['TSFE']->id)) { - break; - } - } + // get current filter + $filters = $this->filters->getFilters(); + foreach ($filters as $filter) { + if ($filter['target_pid'] == intval($GLOBALS['TSFE']->id)) { + break; + } + } - // hook for modifying content - if(is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyMultiselectContent'])) { - foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyMultiselectContent'] as $_classRef) { - $_procObj = & TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); - $content = $_procObj->modifyMultiselectContent($template['multiselect'], $filter, $this); - } - } + // hook for modifying content + if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyMultiselectContent'])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ke_search']['modifyMultiselectContent'] as $_classRef) { + $_procObj = &TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef); + $content = $_procObj->modifyMultiselectContent($template['multiselect'], $filter, $this); + } + } - if(is_array($filter) && count($filter)) { - $contentOptions = ''; - $optionsAmountArray = $GLOBALS['TSFE']->fe_user->getKey('ses', 'ke_search.tagsInSearchResults'); - $countLoops = 1; - if(is_array($this->piVars['filter'][$filter['uid']]) && count($this->piVars['filter'][$filter['uid']])) { - $this->piVars['filter'][$filter['uid']] = array_unique($this->piVars['filter'][$filter['uid']]); - } - foreach($filter['options'] as $optionKey => $option) { - $option['title'] = htmlspecialchars($option['title']); - $option['tag'] = htmlspecialchars($option['tag']); - if($optionsAmountArray[$option['tag']]) { - $optionCounter = $optionsAmountArray[$option['tag']]; - } else $optionCounter = 0; - $selected = ($this->piVars['filter'][$filter['uid']][$optionKey]) ? 'checked="checked"' : ''; - $markerArray['###ADDCLASS###'] = ($countLoops%3) ? '' : ' last'; - $markerArray['###FILTERNAME###'] = 'tx_kesearch_pi1[filter][' . $filter['uid'] . ']'; - $markerArray['###OPTIONID###'] = $option['uid']; - $markerArray['###OPTIONKEY###'] = $optionKey; - $markerArray['###OPTIONTITLE###'] = $option['title'] . ' (' . $optionCounter . ')'; - $markerArray['###OPTIONTAG###'] = $option['tag']; - $markerArray['###SELECTED###'] = $selected; - $countLoops++; - $contentOptions .= $this->cObj->substituteMarkerArray($template['multioption'], $markerArray); - } - $content .= $this->cObj->substituteSubpart( - $template['multifilter'], - '###SUB_FILTER_MULTISELECT_OPTION###', - $contentOptions - ); - $content = $this->cObj->substituteMarker( - $content, - '###TITLE###', - $filter['title'] - ); - } - $content = $this->cObj->substituteSubpart( - $template['multiselect'], - '###SUB_FILTER_MULTISELECT_FILTER###', - $content - ); - $content = $this->cObj->substituteMarker( - $content, - '###FORM_ACTION###', - $this->pi_getPageLink($this->conf['resultPage']) - ); - $content = $this->cObj->substituteMarker( - $content, - '###SHOW_RESULTS###', - $this->pi_getLL('show_results') - ); - $content = $this->cObj->substituteMarker( - $content, - '###LINK_BACK###', - $this->cObj->typoLink( - $this->pi_getLL('back'), - array( - 'parameter' => $this->conf['resultPage'], - 'addQueryString' => 1, - 'addQueryString.' => array( - 'exclude' => 'id' - ) - ) - ) - ); - if(is_array($this->piVars['filter']) && count($this->piVars['filter'])) { - foreach($this->piVars['filter'] as $filterKey => $filterValue) { - if($filterKey == $filter['uid']) continue; - foreach($this->piVars['filter'][$filterKey] as $optionKey => $option) { - $hidden .= $this->cObj->substituteMarker($template['multihidden'], '###NAME###', 'tx_kesearch_pi1[filter][' . $filterKey . '][' . $optionKey . ']'); - $hidden = $this->cObj->substituteMarker($hidden, '###VALUE###', $option); - } - } - } - $content = $this->cObj->substituteSubpart($content, '###SUB_FILTER_MULTISELECT_HIDDEN###', $hidden); - $content = $this->cObj->substituteMarker($content, '###PAGEID###', $this->conf['resultPage']); - $content = $this->cObj->substituteMarker($content, '###SWORD###', htmlspecialchars($this->piVars['sword'])); + if (is_array($filter) && count($filter)) { + $contentOptions = ''; + $optionsAmountArray = $GLOBALS['TSFE']->fe_user->getKey('ses', 'ke_search.tagsInSearchResults'); + $countLoops = 1; + if (is_array($this->piVars['filter'][$filter['uid']]) && count($this->piVars['filter'][$filter['uid']])) { + $this->piVars['filter'][$filter['uid']] = array_unique($this->piVars['filter'][$filter['uid']]); + } + foreach ($filter['options'] as $optionKey => $option) { + $option['title'] = htmlspecialchars($option['title']); + $option['tag'] = htmlspecialchars($option['tag']); + if ($optionsAmountArray[$option['tag']]) { + $optionCounter = $optionsAmountArray[$option['tag']]; + } else { + $optionCounter = 0; + } + $selected = ($this->piVars['filter'][$filter['uid']][$optionKey]) ? 'checked="checked"' : ''; + $markerArray['###ADDCLASS###'] = ($countLoops % 3) ? '' : ' last'; + $markerArray['###FILTERNAME###'] = 'tx_kesearch_pi1[filter][' . $filter['uid'] . ']'; + $markerArray['###OPTIONID###'] = $option['uid']; + $markerArray['###OPTIONKEY###'] = $optionKey; + $markerArray['###OPTIONTITLE###'] = $option['title'] . ' (' . $optionCounter . ')'; + $markerArray['###OPTIONTAG###'] = $option['tag']; + $markerArray['###SELECTED###'] = $selected; + $countLoops++; + $contentOptions .= $this->cObj->substituteMarkerArray($template['multioption'], $markerArray); + } + $content .= $this->cObj->substituteSubpart( + $template['multifilter'], + '###SUB_FILTER_MULTISELECT_OPTION###', + $contentOptions + ); + $content = $this->cObj->substituteMarker( + $content, + '###TITLE###', + $filter['title'] + ); + } + $content = $this->cObj->substituteSubpart( + $template['multiselect'], + '###SUB_FILTER_MULTISELECT_FILTER###', + $content + ); + $content = $this->cObj->substituteMarker( + $content, + '###FORM_ACTION###', + $this->pi_getPageLink($this->conf['resultPage']) + ); + $content = $this->cObj->substituteMarker( + $content, + '###SHOW_RESULTS###', + $this->pi_getLL('show_results') + ); + $content = $this->cObj->substituteMarker( + $content, + '###LINK_BACK###', + $this->cObj->typoLink( + $this->pi_getLL('back'), + array( + 'parameter' => $this->conf['resultPage'], + 'addQueryString' => 1, + 'addQueryString.' => array( + 'exclude' => 'id' + ) + ) + ) + ); + if (is_array($this->piVars['filter']) && count($this->piVars['filter'])) { + foreach ($this->piVars['filter'] as $filterKey => $filterValue) { + if ($filterKey == $filter['uid']) { + continue; + } + foreach ($this->piVars['filter'][$filterKey] as $optionKey => $option) { + $hidden .= $this->cObj->substituteMarker( + $template['multihidden'], + '###NAME###', + 'tx_kesearch_pi1[filter][' . $filterKey . '][' . $optionKey . ']' + ); + $hidden = $this->cObj->substituteMarker($hidden, '###VALUE###', $option); + } + } + } + $content = $this->cObj->substituteSubpart($content, '###SUB_FILTER_MULTISELECT_HIDDEN###', $hidden); + $content = $this->cObj->substituteMarker($content, '###PAGEID###', $this->conf['resultPage']); + $content = $this->cObj->substituteMarker($content, '###SWORD###', htmlspecialchars($this->piVars['sword'])); - return $this->pi_wrapInBaseClass($content); - } -} \ No newline at end of file + return $this->pi_wrapInBaseClass($content); + } +} diff --git a/pi3/class.tx_kesearch_pi3_wizicon.php b/pi3/class.tx_kesearch_pi3_wizicon.php index 12a8c5c0..6ee37d64 100644 --- a/pi3/class.tx_kesearch_pi3_wizicon.php +++ b/pi3/class.tx_kesearch_pi3_wizicon.php @@ -23,46 +23,47 @@ * This copyright notice MUST APPEAR in all copies of the script! * ************************************************************* */ +use \TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + /** * Class that adds the wizard icon. - * - * @author Andreas Kiefer - * @package TYPO3 - * @subpackage tx_kesearch + * @author Andreas Kiefer + * @package TYPO3 + * @subpackage tx_kesearch */ -class tx_kesearch_pi3_wizicon { - - /** - * Processing the wizard items array - * - * @param array $wizardItems: The wizard items - * @return Modified array with wizard items - */ - function proc($wizardItems) { - global $LANG; - $LL = $this->includeLocalLang(); - $extRelPath = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('ke_search'); - - $wizardItems['plugins_tx_kesearch_pi3'] = array( - 'icon' => $extRelPath . 'pi3/ce_wiz.gif', - 'title' => $LANG->getLLL('pi_title', $LL), - 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), - 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi3' - ); +class tx_kesearch_pi3_wizicon +{ - return $wizardItems; - } + /** + * Processing the wizard items array + * @param array $wizardItems : The wizard items + * @return Modified array with wizard items + */ + public function proc($wizardItems) + { + global $LANG; + $LL = $this->includeLocalLang(); + $extRelPath = ExtensionManagementUtility::extRelPath('ke_search'); + $wizardItems['plugins_tx_kesearch_pi3'] = array( + 'icon' => $extRelPath . 'pi3/ce_wiz.gif', + 'title' => $LANG->getLLL('pi_title', $LL), + 'description' => $LANG->getLLL('pi_plus_wiz_description', $LL), + 'params' => '&defVals[tt_content][CType]=list&defVals[tt_content][list_type]=ke_search_pi3' + ); - /** - * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. - * - * @return The array with language labels - */ - function includeLocalLang() { - $llFile = TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ke_search') . 'pi3/locallang.xml'; - $xmlParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); - $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); - return $LOCAL_LANG; - } + return $wizardItems; + } + /** + * Reads the [extDir]/locallang.xml and returns the \$LOCAL_LANG array found in that file. + * @return The array with language labels + */ + public function includeLocalLang() + { + $llFile = ExtensionManagementUtility::extPath('ke_search') . 'pi3/locallang.xml'; + $xmlParser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Localization\\Parser\\LocallangXmlParser'); + $LOCAL_LANG = $xmlParser->getParsedData($llFile, $GLOBALS['LANG']->lang); + return $LOCAL_LANG; + } } diff --git a/res/scripts/SVG.php b/res/scripts/SVG.php index 84214a19..a22dfae8 100644 --- a/res/scripts/SVG.php +++ b/res/scripts/SVG.php @@ -1,12 +1,12 @@ - - + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:ev="http://www.w3.org/2001/xml-events" + version="1.1" baseProfile="full" + width="50px" height="12px" viewBox="0 0 50 12"> + +