1<?php
2namespace TYPO3\CMS\IndexedSearch\ViewHelpers;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17use TYPO3\CMS\Core\Utility\GeneralUtility;
18use TYPO3\CMS\Core\Utility\MathUtility;
19use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
20use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
21use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
22use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
23
24/**
25 * Page browser for indexed search, and only useful here, as the
26 * regular pagebrowser
27 * so this is a cleaner "pi_browsebox" but not a real page browser
28 * functionality
29 * @internal
30 */
31class PageBrowsingViewHelper extends AbstractViewHelper
32{
33    use CompileWithRenderStatic;
34
35    /**
36     * As this ViewHelper renders HTML, the output must not be escaped.
37     *
38     * @var bool
39     */
40    protected $escapeOutput = false;
41
42    /**
43     * @var string
44     */
45    protected static $prefixId = 'tx_indexedsearch';
46
47    /**
48     * Initialize arguments
49     */
50    public function initializeArguments()
51    {
52        $this->registerArgument('maximumNumberOfResultPages', 'int', '', true);
53        $this->registerArgument('numberOfResults', 'int', '', true);
54        $this->registerArgument('resultsPerPage', 'int', '', true);
55        $this->registerArgument('currentPage', 'int', '', false, 0);
56        $this->registerArgument('freeIndexUid', 'int', '');
57    }
58
59    /**
60     * @param array $arguments
61     * @param \Closure $renderChildrenClosure
62     * @param RenderingContextInterface $renderingContext
63     *
64     * @return string
65     */
66    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
67    {
68        $maximumNumberOfResultPages = $arguments['maximumNumberOfResultPages'];
69        $numberOfResults = $arguments['numberOfResults'];
70        $resultsPerPage = $arguments['resultsPerPage'];
71        $currentPage = $arguments['currentPage'];
72        $freeIndexUid = $arguments['freeIndexUid'];
73
74        if ($resultsPerPage <= 0) {
75            $resultsPerPage = 10;
76        }
77        $pageCount = (int)ceil($numberOfResults / $resultsPerPage);
78        // only show the result browser if more than one page is needed
79        if ($pageCount === 1) {
80            return '';
81        }
82
83        // Check if $currentPage is in range
84        $currentPage = MathUtility::forceIntegerInRange($currentPage, 0, $pageCount - 1);
85
86        $content = '';
87        // prev page
88        // show on all pages after the 1st one
89        if ($currentPage > 0) {
90            $label = LocalizationUtility::translate('displayResults.previous', 'IndexedSearch');
91            $content .= '<li>' . self::makecurrentPageSelector_link($label, $currentPage - 1, $freeIndexUid) . '</li>';
92        }
93        // Check if $maximumNumberOfResultPages is in range
94        $maximumNumberOfResultPages = MathUtility::forceIntegerInRange($maximumNumberOfResultPages, 1, $pageCount, 10);
95        // Assume $currentPage is in the middle and calculate the index limits of the result page listing
96        $minPage = $currentPage - (int)floor($maximumNumberOfResultPages / 2);
97        $maxPage = $minPage + $maximumNumberOfResultPages - 1;
98        // Check if the indexes are within the page limits
99        if ($minPage < 0) {
100            $maxPage -= $minPage;
101            $minPage = 0;
102        } elseif ($maxPage >= $pageCount) {
103            $minPage -= $maxPage - $pageCount + 1;
104            $maxPage = $pageCount - 1;
105        }
106        $pageLabel = LocalizationUtility::translate('displayResults.page', 'IndexedSearch');
107        for ($a = $minPage; $a <= $maxPage; $a++) {
108            $label = trim($pageLabel . ' ' . ($a + 1));
109            $label = self::makecurrentPageSelector_link($label, $a, $freeIndexUid);
110            if ($a === $currentPage) {
111                $content .= '<li class="tx-indexedsearch-browselist-currentPage"><strong>' . $label . '</strong></li>';
112            } else {
113                $content .= '<li>' . $label . '</li>';
114            }
115        }
116        // next link
117        if ($currentPage < $pageCount - 1) {
118            $label = LocalizationUtility::translate('displayResults.next', 'IndexedSearch');
119            $content .= '<li>' . self::makecurrentPageSelector_link($label, $currentPage + 1, $freeIndexUid) . '</li>';
120        }
121        return '<ul class="tx-indexedsearch-browsebox">' . $content . '</ul>';
122    }
123
124    /**
125     * Used to make the link for the result-browser.
126     * Notice how the links must resubmit the form after setting the new currentPage-value in a hidden formfield.
127     *
128     * @param string $str String to wrap in <a> tag
129     * @param int $p currentPage value
130     * @param string $freeIndexUid List of integers pointing to free indexing configurations to search. -1 represents no filtering, 0 represents TYPO3 pages only, any number above zero is a uid of an indexing configuration!
131     * @return string Input string wrapped in <a> tag with onclick event attribute set.
132     */
133    protected static function makecurrentPageSelector_link($str, $p, $freeIndexUid)
134    {
135        $onclick = 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId . '_pointer') . ').value=' . GeneralUtility::quoteJSvalue($p) . ';';
136        if ($freeIndexUid !== null) {
137            $onclick .= 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId . '_freeIndexUid') . ').value=' . GeneralUtility::quoteJSvalue($freeIndexUid) . ';';
138        }
139        $onclick .= 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId) . ').submit();return false;';
140        return '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . htmlspecialchars($str) . '</a>';
141    }
142}
143