[FEATURE] Make path separator of search result configurable 01/36001/14
authorFrederic Gaus <gaus@flagbit.de>
Tue, 13 Jan 2015 15:02:14 +0000 (16:02 +0100)
committerBenni Mack <benni@typo3.org>
Tue, 6 Oct 2015 20:50:35 +0000 (22:50 +0200)
This patch makes the hard coded path separator ("/") of the
indexed_search search result configurable with TypoScript.
You can use option split syntax to wrap each page title separately.

It also makes getPathFromPageId() return already
htmlspecialcharsed string.

Resolves: #23156
Releases: master
Change-Id: I76dc55518ffb0702d669b043249dc70fc9492d82
Reviewed-on: http://review.typo3.org/36001
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-23156-IndexedSearchMakePathSeparatorOfSearchResultConfigurable.rst [new file with mode: 0644]
typo3/sysext/indexed_search/Classes/Controller/SearchController.php
typo3/sysext/indexed_search/Classes/Controller/SearchFormController.php
typo3/sysext/indexed_search/Configuration/TypoScript/setup.txt
typo3/sysext/indexed_search/Documentation/Configuration/TypoScript/Index.rst
typo3/sysext/indexed_search/ext_typoscript_setup.txt

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-23156-IndexedSearchMakePathSeparatorOfSearchResultConfigurable.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-23156-IndexedSearchMakePathSeparatorOfSearchResultConfigurable.rst
new file mode 100644 (file)
index 0000000..a22417f
--- /dev/null
@@ -0,0 +1,21 @@
+===================================================================================
+Feature: #23156 - Indexed search: Make path separator of search result configurable
+===================================================================================
+
+Description
+===========
+
+A new TypoScript configuration option :ts:``breadcrumbWrap`` has been added. It allows to configure page path separator used in breadcrumbs in Indexed Search results. This option supports TypoScript option split syntax.
+
+
+Impact
+======
+
+By default Indexed Search is configured to use "/" as a path separator, so it's backward compatible.
+Use following configuration for Indexed Search Extbase plugin:
+.. code-block:: ts
+       plugin.tx_indexedsearch.settings.breadcrumbWrap = / || /
+
+For plugin based on AbstractPlugin use:
+.. code-block:: ts
+       plugin.tx_indexedsearch.breadcrumbWrap = / || /
\ No newline at end of file
index 05ce85c..4f1b144 100644 (file)
@@ -294,7 +294,7 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                }
                // Print a message telling which words in which sections we searched for
                if (substr($this->searchData['sections'], 0, 2) === 'rl') {
-                       $result['searchedInSectionInfo'] = LocalizationUtility::translate('result.inSection', 'IndexedSearch') . ' "' . substr($this->getPathFromPageId(substr($this->searchData['sections'], 4)), 1) . '"';
+                       $result['searchedInSectionInfo'] = LocalizationUtility::translate('result.inSection', 'IndexedSearch') . ' "' . $this->getPathFromPageId(substr($this->searchData['sections'], 4)) . '"';
                }
                return $result;
        }
@@ -359,7 +359,7 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                                        $sectionTitleLinked = LocalizationUtility::translate('result.unnamedSection', 'IndexedSearch') . ':';
                                } else {
                                        $onclick = 'document.forms[\'tx_indexedsearch\'][\'tx_indexedsearch_pi2[search][_sections]\'].value=' . GeneralUtility::quoteJSvalue($theRLid) . ';document.forms[\'tx_indexedsearch\'].submit();return false;';
-                                       $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . htmlspecialchars($sectionName) . ':</a>';
+                                       $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . $sectionName . ':</a>';
                                }
                                $resultRowsCount = count($resultRows);
                                $this->resultSections[$id] = array($sectionName, $resultRowsCount);
@@ -1264,8 +1264,8 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
         * Returns the path to the page $id
         *
         * @param int $id Page ID
-        * @param string $pathMP MP variable content
-        * @return string Path
+        * @param string $pathMP Content of the MP (mount point) variable
+        * @return string Path (HTML-escaped)
         */
        protected function getPathFromPageId($id, $pathMP = '') {
                $identStr = $id . '|' . $pathMP;
@@ -1274,7 +1274,10 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                        $this->domainRecords[$id] = array();
                        $rl = $GLOBALS['TSFE']->sys_page->getRootLine($id, $pathMP);
                        $path = '';
+                       $pageCount = count($rl);
                        if (is_array($rl) && !empty($rl)) {
+                               $breadcrumbWrap = isset($this->settings['breadcrumbWrap']) ? $this->settings['breadcrumbWrap'] : '/';
+                               $breadcrumbWraps = $GLOBALS['TSFE']->tmpl->splitConfArray(array('wrap' => $breadcrumbWrap), $pageCount);
                                foreach ($rl as $k => $v) {
                                        // Check fe_user
                                        if ($v['fe_group'] && ($v['uid'] == $id || $v['extendToSubpages'])) {
@@ -1292,9 +1295,10 @@ class SearchController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
                                        }
                                        // Stop, if we find that the current id is the current root page.
                                        if ($v['uid'] == $GLOBALS['TSFE']->config['rootLine'][0]['uid']) {
+                                               array_pop($breadcrumbWraps);
                                                break;
                                        }
-                                       $path = '/' . $v['title'] . $path;
+                                       $path = $GLOBALS['TSFE']->cObj->wrap(htmlspecialchars($v['title']), array_pop($breadcrumbWraps)['wrap']) . $path;
                                }
                        }
                        $this->pathCache[$identStr] = $path;
index 003c4b8..9b74f5a 100755 (executable)
@@ -678,7 +678,7 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                        $content .= '<p' . $this->pi_classParam('noresults') . '>' . $this->pi_getLL('noResults', '', TRUE) . '</p>';
                }
                // Print a message telling which words we searched for, and in which sections etc.
-               $what = $this->tellUsWhatIsSeachedFor($sWArr) . (substr($this->piVars['sections'], 0, 2) == 'rl' ? ' ' . $this->pi_getLL('inSection', '', TRUE) . ' "' . substr($this->getPathFromPageId(substr($this->piVars['sections'], 4)), 1) . '"' : '');
+               $what = $this->tellUsWhatIsSeachedFor($sWArr) . (substr($this->piVars['sections'], 0, 2) == 'rl' ? ' ' . $this->pi_getLL('inSection', '', TRUE) . ' "' . $this->getPathFromPageId(substr($this->piVars['sections'], 4)) . '"' : '');
                $what = '<div' . $this->pi_classParam('whatis') . '>' . $this->cObj->stdWrap($what, $this->conf['whatis_stdWrap.']) . '</div>';
                $content = $what . $content;
                // Return content:
@@ -741,9 +741,9 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                                                } elseif ($this->conf['linkSectionTitles']) {
                                                        $quotedPrefix = GeneralUtility::quoteJSvalue($this->prefixId);
                                                        $onclick = 'document.forms[' . $quotedPrefix . '][' . GeneralUtility::quoteJSvalue($this->prefixId . '[_sections]') . '].value=' . GeneralUtility::quoteJSvalue($theRLid) . ';document.forms[' . $quotedPrefix . '].submit();return false;';
-                                                       $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . htmlspecialchars($sectionName) . ':</a>';
+                                                       $sectionTitleLinked = '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . $sectionName . ':</a>';
                                                } else {
-                                                       $sectionTitleLinked = htmlspecialchars($sectionName);
+                                                       $sectionTitleLinked = $sectionName;
                                                }
                                                $resultRowsCount = count($resultRows);
                                                $this->resultSections[$id] = array($sectionName, $resultRowsCount);
@@ -1503,7 +1503,7 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                foreach ($this->resultSections as $id => $dat) {
                        $markerArray = array();
                        $aBegin = '<a href="' . htmlspecialchars($anchorPrefix . '#anchor_' . md5($id)) . '">';
-                       $aContent = htmlspecialchars((trim($dat[0]) ? trim($dat[0]) : $this->pi_getLL('unnamedSection'))) . ' (' . $dat[1] . ' ' . $this->pi_getLL(($dat[1] > 1 ? 'word_pages' : 'word_page'), '', TRUE) . ')';
+                       $aContent = (trim($dat[0]) ? trim($dat[0]) : htmlspecialchars($this->pi_getLL('unnamedSection'))) . ' (' . $dat[1] . ' ' . $this->pi_getLL(($dat[1] > 1 ? 'word_pages' : 'word_page'), '', TRUE) . ')';
                        $aEnd = '</a>';
                        $markerArray['###LINK###'] = $aBegin . $aContent . $aEnd;
                        $links[] = $this->cObj->substituteMarkerArrayCached($item, $markerArray, array(), array());
@@ -2037,7 +2037,7 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                        }
                        $tmplArray['path'] = '<a href="' . htmlspecialchars($row['data_filename']) . '"' . $targetAttribute . '>' . htmlspecialchars($row['data_filename']) . '</a>';
                } else {
-                       $pathStr = htmlspecialchars($this->getPathFromPageId($pathId, $pathMP));
+                       $pathStr = $this->getPathFromPageId($pathId, $pathMP);
                        $tmplArray['path'] = $this->linkPage($pathId, $pathStr, array(
                                'cHashParams' => $row['cHashParams'],
                                'data_page_type' => $row['data_page_type'],
@@ -2182,7 +2182,7 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
         *
         * @param int $id Page ID
         * @param string $pathMP MP variable content
-        * @return string Path
+        * @return string Path (HTML-escaped)
         */
        public function getPathFromPageId($id, $pathMP = '') {
                $identStr = $id . '|' . $pathMP;
@@ -2190,9 +2190,12 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                        $this->fe_groups_required[$id] = array();
                        $this->domain_records[$id] = array();
                        $rl = $this->getRootLine($id, $pathMP);
-                       $hitRoot = 0;
                        $path = '';
+                       $pageCount = count($rl);
                        if (is_array($rl) && !empty($rl)) {
+                               $index = 0;
+                               $breadcrumbWrap = isset($this->conf['breadcrumbWrap']) ? $this->conf['breadcrumbWrap'] : '/';
+                               $breadcrumbWraps = $GLOBALS['TSFE']->tmpl->splitConfArray(array('wrap' => $breadcrumbWrap), $pageCount);
                                foreach ($rl as $k => $v) {
                                        // Check fe_user
                                        if ($v['fe_group'] && ($v['uid'] == $id || $v['extendToSubpages'])) {
@@ -2210,9 +2213,10 @@ class SearchFormController extends \TYPO3\CMS\Frontend\Plugin\AbstractPlugin {
                                        }
                                        // Stop, if we find that the current id is the current root page.
                                        if ($v['uid'] == $this->frontendController->config['rootLine'][0]['uid']) {
+                                               array_pop($breadcrumbWraps);
                                                break;
                                        }
-                                       $path = '/' . $v['title'] . $path;
+                                       $path = $this->cObj->wrap(htmlspecialchars($v['title']), array_pop($breadcrumbWraps)['wrap']) . $path;
                                }
                        }
                        $this->cache_path[$identStr] = $path;
index 76b911b..81fb68b 100644 (file)
@@ -16,6 +16,8 @@ plugin.tx_indexedsearch {
                # show the number of results
                displayResultNumber = 0
 
+               breadcrumbWrap = / || /
+
                # show the parse times
                displayParsetimes = 0
                displayLevel1Sections = 1
index 1fe576c..ee68844 100644 (file)
@@ -33,6 +33,25 @@ templateFile
          The template file, see examples in typo3/sysext/indexed\_search/pi/.
 
 
+.. _breadcrumbWrap:
+
+breadcrumbWrap
+""""""""""""""
+
+.. container:: table-row
+
+   Property
+         breadcrumbWrap
+
+   Data type
+         :ref:`wrap <t3tsref:wrap>` + :ref:`optionSplit <t3tsref:objects-optionsplit>`
+
+   Description
+         This configuration is used to wrap a single page title in a search result item breadcrumb.
+
+   Default
+         / || /
+
 
 .. _show-forbiddenrecords:
 
index 34fb937..40b9502 100644 (file)
@@ -11,6 +11,8 @@ plugin.tx_indexedsearch {
                modified =
        }
 
+       breadcrumbWrap = / || /
+
        show {
                rules = 1
                parsetimes = 0