diff --git a/Classes/Controller/Ajax/JoblistController.php b/Classes/Controller/Ajax/JoblistController.php index ce9b84a17807046b0c5ecdd6fa131953233dece4..b238999d5fc280b5164dcf445f66064a069f945b 100644 --- a/Classes/Controller/Ajax/JoblistController.php +++ b/Classes/Controller/Ajax/JoblistController.php @@ -70,7 +70,10 @@ class JoblistController extends AbstractAjaxController { $this->assignFilterValues($recordPageId); // set all filtered jobs - $this->view->assign('jobs', FrontendFilterService::getJobs($filters, $recordPageId)); + $jobs = FrontendFilterService::getJobs($filters, $recordPageId); + $this->view->assign('jobs', $jobs); + $this->view->assign('numberOfPages', \count($jobs)); + // set default selected values $this->view->assign('selectedCountry', $filters['country']); diff --git a/Classes/Controller/JoblistController.php b/Classes/Controller/JoblistController.php index 3928a4fe391c4ec15e68cb1ce2b5390fb79e0f58..f3994a1b78368806aa029c5b2efefab891345a6b 100644 --- a/Classes/Controller/JoblistController.php +++ b/Classes/Controller/JoblistController.php @@ -70,6 +70,7 @@ class JoblistController extends ActionController { // get all jobs for the next root page $jobs = $this->jobRepository->findJobs($storagePid); $this->view->assign('jobs', $jobs); + $this->view->assign('numberOfPages', \count($jobs)); } /** diff --git a/Classes/Controller/PageBrowserController.php b/Classes/Controller/PageBrowserController.php new file mode 100644 index 0000000000000000000000000000000000000000..5dd2a3968cfcd9ed3c5822d0b75b1d8ddd72eb98 --- /dev/null +++ b/Classes/Controller/PageBrowserController.php @@ -0,0 +1,146 @@ +<?php + +namespace SGalinski\SgJobs\Controller; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * 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 3 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\Extbase\Mvc\Controller\ActionController; + +/** + * Controller that handles the page browser. + */ +class PageBrowserController extends ActionController { + /** + * @var string + */ + protected $pageParameterName = ''; + + /** + * @var int + */ + protected $numberOfPages = 0; + + /** + * @var int + */ + protected $currentPage = 0; + + /** + * @var int + */ + protected $pagesBefore = 0; + + /** + * @var int + */ + protected $pagesAfter = 0; + + /** + * @var bool + */ + protected $enableMorePages = FALSE; + + /** + * @var bool + */ + protected $enableLessPages = FALSE; + + /** + * Renders the index view. + * + * @param int $currentPage + * @return void + */ + public function indexAction($currentPage = 0) { + $this->currentPage = (int) $currentPage; + + $this->initializeConfiguration(); + $this->addDataToView(); + } + + /** + * Initializes the configuration. + * + * @return void + */ + protected function initializeConfiguration() { + $this->pageParameterName = 'tx_sgjobs_pagebrowser[currentPage]'; + $this->numberOfPages = (int) $this->settings['numberOfPages']; + $this->pagesBefore = (int) $this->settings['pagesBefore']; + $this->pagesAfter = (int) $this->settings['pagesAfter']; + $this->enableMorePages = (bool) $this->settings['enableMorePages']; + $this->enableLessPages = (bool) $this->settings['enableLessPages']; + } + + /** + * Adds the necessary data to the view. + * + * @return void + */ + protected function addDataToView() { + if ($this->numberOfPages <= 1) { + $this->view = NULL; + return; + } + + $pageLinks = []; + $start = max($this->currentPage - $this->pagesBefore, 0); + $end = min($this->numberOfPages, $this->currentPage + $this->pagesAfter + 1); + for ($i = $start; $i < $end; $i++) { + $pageLinks[] = [ + 'number' => $i + 1, + 'link' => $this->getPageLink($i), + 'isCurrentPage' => $i === $this->currentPage, + ]; + } + + $this->view->assignMultiple( + [ + 'enableLessPages' => $this->enableLessPages, + 'enableMorePages' => $this->enableMorePages, + 'previousLink' => $this->getPageLink($this->currentPage - 1), + 'nextLink' => $this->getPageLink($this->currentPage + 1), + 'enableLessPagesLink' => $this->getPageLink($this->currentPage - $this->pagesBefore - 1), + 'enableMorePagesLink' => $this->getPageLink($this->currentPage + $this->pagesAfter + 1), + 'pageLinks' => $pageLinks, + 'prevPageExist' => $this->currentPage > 0, + 'showLessPages' => ($this->currentPage - $this->pagesBefore) > 0, + 'showNextPages' => ($this->currentPage + $this->pagesAfter + 1) < $this->numberOfPages, + 'nextPageExist' => $this->currentPage < ($this->numberOfPages - 1), + ] + ); + } + + /** + * Generates page link. Keeps all current URL parameters except for cHash and tx_pagebrowse_pi1[page]. + * + * @param int $page Page number starting from 1 + * @return string + */ + protected function getPageLink($page): string { + return $this->uriBuilder->reset()->setAddQueryString(TRUE)->setUseCacheHash(TRUE) + ->uriFor('index', ['currentPage' => $page,]); + } +} diff --git a/Classes/ViewHelpers/ExtendedIfViewHelper.php b/Classes/ViewHelpers/ExtendedIfViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..331b3eef24b9a0719445fa73bd579438d1550f58 --- /dev/null +++ b/Classes/ViewHelpers/ExtendedIfViewHelper.php @@ -0,0 +1,97 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * 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 3 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\VersionNumberUtility; +use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper; + +/** + * ExtendedIfViewHelper + */ +class ExtendedIfViewHelper extends AbstractConditionViewHelper { + /** + * Initializes the "then" and "else" arguments + * + * @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception + */ + public function initializeArguments() { + parent::initializeArguments(); + if (VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000) { + $this->registerArgument( + 'condition', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + } + $this->registerArgument( + 'or', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + $this->registerArgument( + 'or2', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + $this->registerArgument( + 'or3', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + $this->registerArgument( + 'or4', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + $this->registerArgument( + 'and', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, TRUE + ); + $this->registerArgument( + 'and2', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, TRUE + ); + $this->registerArgument( + 'and3', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, TRUE + ); + $this->registerArgument( + 'and4', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, TRUE + ); + $this->registerArgument( + 'negate', 'boolean', 'Condition expression conforming to Fluid boolean rules', FALSE, FALSE + ); + } + + /** + * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality. + * + * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method. + * @return bool + */ + protected static function evaluateCondition($arguments = NULL): bool { + $conditionResult = ( + (isset($arguments['condition']) && $arguments['condition']) || + (isset($arguments['or']) && $arguments['or']) || + (isset($arguments['or2']) && $arguments['or2']) || + (isset($arguments['or3']) && $arguments['or3']) || + (isset($arguments['or4']) && $arguments['or4']) + ) && isset($arguments['and']) && $arguments['and'] && + isset($arguments['and2']) && $arguments['and2'] && + isset($arguments['and3']) && $arguments['and3'] && + isset($arguments['and4']) && $arguments['and4']; + + return isset($arguments['negate']) && $arguments['negate'] ? !$conditionResult : $conditionResult; + } +} diff --git a/Classes/ViewHelpers/PageBrowserViewHelper.php b/Classes/ViewHelpers/PageBrowserViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..a380a2965b3c803ca92b2863364d7853d0dd89e9 --- /dev/null +++ b/Classes/ViewHelpers/PageBrowserViewHelper.php @@ -0,0 +1,56 @@ +<?php + +namespace SGalinski\SgJobs\ViewHelpers; + +/*************************************************************** + * Copyright notice + * + * (c) sgalinski Internet Services (https://www.sgalinski.de) + * + * 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 3 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\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; + +/** + * View helper that renders a page browser based upon the pagebrowse extension. + * + * Example: + * {namespace sg=SGalinski\SgJobs\ViewHelpers} + * <sg:pageBrowser numberOfPages="" /> + */ +class PageBrowserViewHelper extends AbstractViewHelper { + /** + * Render method of the view helper. + * + * @param integer $numberOfPages + * @return string + * @throws \UnexpectedValueException + */ + public function render($numberOfPages): string { + $configuration = $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_sgjobs.']['pagebrowser.']; + $configuration['settings.']['numberOfPages'] = (int) $numberOfPages; + + /** @var ContentObjectRenderer $contentObject */ + $contentObject = $this->objectManager->get(ContentObjectRenderer::class); + $contentObject->start([]); + return $contentObject->cObjGetSingle('USER', $configuration); + } +} diff --git a/Configuration/TypoScript/Frontend/constants.ts b/Configuration/TypoScript/Frontend/constants.ts index f5b525272f662c3ccd6d97b34c3bdd3ab3a7af6f..f991c3f562ebec8cc18cf5d3697d9e0810bd4a98 100644 --- a/Configuration/TypoScript/Frontend/constants.ts +++ b/Configuration/TypoScript/Frontend/constants.ts @@ -18,4 +18,18 @@ plugin.tx_sgjobs { # cat=plugin.tx_sgjobs/other; type=string; label=Allowed mime types for uploads in the Fluid template (comma separated) allowedMimeTypes = application/pdf } + + pagebrowser.settings { + # Number of page links to show before the current page + pagesBefore = 1 + + # Number of page links to show before the current page + pagesAfter = 1 + + # Enables section for "more" pages. This section is shown after links to next pages, normally like three dots (1 2 3 ...). Notice that you can also hide it by emptying corresponding template section. + enableMorePages = 1 + + # Enables section for "less" pages. This section is shown after links to next pages, normally like three dots (... 1 2 3) Notice that you can also hide it by emptying corresponding template section. + enableLessPages = 1 + } } diff --git a/Configuration/TypoScript/Frontend/setup.ts b/Configuration/TypoScript/Frontend/setup.ts index 7837b6071b48c251490cc14e85e5a8c318cdea26..c2aee221595406248bc72e918e2b0119907fd9d2 100644 --- a/Configuration/TypoScript/Frontend/setup.ts +++ b/Configuration/TypoScript/Frontend/setup.ts @@ -34,4 +34,39 @@ plugin.tx_sgjobs { legacy { enableLegacyFlashMessageHandling = 0 } + + pagebrowser = USER + pagebrowser { + userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run + extensionName = SgJobs + pluginName = PageBrowser + vendorName = SGalinski + controller = PageBrowser + action = index + view < plugin.tx_sgjobs.view + persistence < plugin.tx_sgjobs.persistence + features < plugin.tx_sgjobs.features + # cHash can't be generated for pageBrowser call; will cause a 404 error if not set to 0 + features.requireCHashArgumentForActionArguments = 0 + legacy < plugin.tx_sgjobs.legacy + settings { + # Number of page links to show before the current page + pagesBefore = {$plugin.tx_sgjobs.pagebrowser.settings.pagesBefore} + + # Number of page links to show before the current page + pagesAfter = {$plugin.tx_sgjobs.pagebrowser.settings.pagesAfter} + + # Enables section for "more" pages. This section is shown after links to next pages, normally like three dots (1 2 3 ...). Notice that you can also hide it by emptying corresponding template section. + enableMorePages = {$plugin.tx_sgjobs.pagebrowser.settings.enableMorePages} + + # Enables section for "less" pages. This section is shown after links to next pages, normally like three dots (... 1 2 3) Notice that you can also hide it by emptying corresponding template section. + enableLessPages = {$plugin.tx_sgjobs.pagebrowser.settings.enableLessPages} + + # The number of the pages + numberOfPages = 0 + + # The current page + currentPage = 0 + } + } } diff --git a/Resources/Private/Layouts/PageBrowser.html b/Resources/Private/Layouts/PageBrowser.html new file mode 100644 index 0000000000000000000000000000000000000000..1297e8bed3cac53993ce21a270b61f599097a92a --- /dev/null +++ b/Resources/Private/Layouts/PageBrowser.html @@ -0,0 +1,3 @@ +<div class="tx-sgjobs-pagebrowser"> + <f:render section="main" /> +</div> diff --git a/Resources/Private/Templates/Joblist/Index.html b/Resources/Private/Templates/Joblist/Index.html index 810d70fe855533691f66ba2c9ef1938583828e8b..a452fea7c43741d53342be6e07af7e29e3d137b2 100644 --- a/Resources/Private/Templates/Joblist/Index.html +++ b/Resources/Private/Templates/Joblist/Index.html @@ -1,9 +1,8 @@ +{namespace sg=SGalinski\SgJobs\ViewHelpers} <f:layout name="Default" /> - <f:section name="main"> <div id="sgjobs-joblist"> - <f:render partial="Filter" arguments="{filters: filters, countries: countries, cities: cities, companies: companies, areas: areas, functions: functions}" @@ -13,6 +12,10 @@ <f:render partial="Job" arguments="{job: job}" /> <hr> </f:for> + + <f:format.raw> + <sg:pageBrowser numberOfPages="{numberOfPages}" /> + </f:format.raw> </div> <div id="sgjobs-apply-nojob"> <f:form action="applyForm" controller="Joblist" pluginName="JobApplication" pageUid="{settings.applyPage}" objectName="jobData"> diff --git a/Resources/Private/Templates/PageBrowser/Index.html b/Resources/Private/Templates/PageBrowser/Index.html new file mode 100644 index 0000000000000000000000000000000000000000..fa04a25f535f7d4e153b019f383d33e9b852326c --- /dev/null +++ b/Resources/Private/Templates/PageBrowser/Index.html @@ -0,0 +1,85 @@ +{namespace sg=SGalinski\SgJobs\ViewHelpers} + +<f:layout name="PageBrowser" /> + +<f:section name="main"> + <nav> + <ul class="pagination"> + <f:if condition="{prevPageExist}"> + <f:then> + <li class="tx-pagebrowse-prev"> + <a href="{previousLink}" aria-label="Previous"> + « + </a> + </li> + </f:then> + <f:else> + <li class="tx-pagebrowse-prev disabled"> + <a aria-label="Previous"> + <span aria-hidden="true"> + « + </span> + </a> + </li> + </f:else> + </f:if> + + <sg:extendedIf condition="{enableLessPages}" and="{showLessPages}"> + <li> + <a href="{enableLessPagesLink}"> + ... + </a> + </li> + </sg:extendedIf> + + <f:for each="{pageLinks}" as="pageLink"> + <f:if condition="{pageLink.isCurrentPage}"> + <f:then> + <li class="tx-pagebrowse-current active"> + <a href="#"> + {pageLink.number} + <span class="sr-only"> + (current) + </span> + </a> + </li> + </f:then> + <f:else> + <li class="tx-pagebrowse-page"> + <a href="{pageLink.link}"> + {pageLink.number} + </a> + </li> + </f:else> + </f:if> + </f:for> + + <sg:extendedIf condition="{enableMorePages}" and="{showNextPages}"> + <li> + <a href="{enableMorePagesLink}"> + ... + </a> + </li> + </sg:extendedIf> + + <f:if condition="{nextPageExist}"> + <f:then> + <li class="tx-pagebrowse-next"> + <a href="{nextLink}" aria-label="Next"> + » + </a> + </li> + </f:then> + <f:else> + <li class="tx-pagebrowse-next disabled"> + <a aria-label="Next"> + <span aria-hidden="true"> + » + </span> + </a> + </li> + </f:else> + </f:if> + </ul> + </nav> +</f:section> diff --git a/ext_localconf.php b/ext_localconf.php index b15755ae847f14c47e5234ab05cc343236a48f52..170399efcb60fdf3b1b05fa1c676eafb81d79b93 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -26,14 +26,21 @@ ] ); +\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'SGalinski.sg_jobs', + 'PageBrowser', + ['PageBrowser' => 'index',], + ['PageBrowser' => '',] +); + // Configure ajax controller -\SGalinski\SgAjax\Service\AjaxRegistration::configureAjaxFrontendPlugin('sg_jobs', [ +\SGalinski\SgAjax\Service\AjaxRegistration::configureAjaxFrontendPlugin( + 'sg_jobs', [ 'Ajax\Joblist' => 'filter', 'Ajax\JobApplication' => 'deleteTempFile', ] ); - if (TYPO3_MODE === 'BE') { $extPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('sg_jobs'); $tsPath = $extPath . 'Configuration/TypoScript/Backend/';