mirror of
https://github.com/werkraum-media/events.git
synced 2024-11-10 03:56:09 +01:00
Keep filter during pagination
This commit is contained in:
parent
10df1eddc1
commit
be56f0fd12
12 changed files with 156 additions and 93 deletions
|
@ -6,6 +6,7 @@ use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
|
|||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Annotation as Extbase;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Service\ExtensionService;
|
||||
use Wrm\Events\Domain\Model\Date;
|
||||
use Wrm\Events\Domain\Model\Dto\DateDemand;
|
||||
use Wrm\Events\Domain\Model\Dto\DateDemandFactory;
|
||||
|
@ -42,11 +43,6 @@ class DateController extends AbstractController
|
|||
*/
|
||||
protected $categoryRepository;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
|
@ -57,22 +53,34 @@ class DateController extends AbstractController
|
|||
*/
|
||||
protected $dataProcessing;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* @var ExtensionService
|
||||
*/
|
||||
protected $extensionService;
|
||||
|
||||
public function __construct(
|
||||
DateDemandFactory $demandFactory,
|
||||
RegionRepository $regionRepository,
|
||||
DateRepository $dateRepository,
|
||||
RegionRepository $regionRepository,
|
||||
CategoryRepository $categoryRepository,
|
||||
Factory $paginationFactory,
|
||||
DataProcessingForModels $dataProcessing,
|
||||
EventDispatcher $eventDispatcher,
|
||||
Factory $paginationFactory
|
||||
ExtensionService $extensionService
|
||||
) {
|
||||
$this->demandFactory = $demandFactory;
|
||||
$this->regionRepository = $regionRepository;
|
||||
$this->dateRepository = $dateRepository;
|
||||
$this->regionRepository = $regionRepository;
|
||||
$this->categoryRepository = $categoryRepository;
|
||||
$this->paginationFactory = $paginationFactory;
|
||||
$this->dataProcessing = $dataProcessing;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->paginationFactory = $paginationFactory;
|
||||
$this->extensionService = $extensionService;
|
||||
}
|
||||
|
||||
protected function initializeAction(): void
|
||||
|
@ -82,6 +90,8 @@ class DateController extends AbstractController
|
|||
$this->demandFactory->setContentObjectRenderer($contentObject);
|
||||
}
|
||||
$this->dataProcessing->setConfigurationManager($this->configurationManager);
|
||||
|
||||
$this->handlePostRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,14 +105,6 @@ class DateController extends AbstractController
|
|||
$demand = $this->demandFactory->fromSettings($this->settings);
|
||||
if ($search !== []) {
|
||||
$demand = DateDemand::createFromRequestValues($search, $this->settings);
|
||||
} elseif (
|
||||
($this->request->hasArgument('searchword') && $this->request->getArgument('searchword') != '')
|
||||
|| ($this->request->hasArgument('region') && $this->request->getArgument('region') != '')
|
||||
|| ($this->request->hasArgument('start') && $this->request->getArgument('start') != '')
|
||||
|| ($this->request->hasArgument('end') && $this->request->getArgument('end') != '')
|
||||
|| ($this->request->hasArgument('events_search') && $this->request->getArgument('events_search') != [])
|
||||
) {
|
||||
$demand = $this->createDemandFromSearch();
|
||||
}
|
||||
|
||||
$dates = $this->dateRepository->findByDemand($demand);
|
||||
|
@ -128,24 +130,6 @@ class DateController extends AbstractController
|
|||
*/
|
||||
public function searchAction(array $search = []): void
|
||||
{
|
||||
$arguments = GeneralUtility::_GET('tx_events_datelist') ?? $search;
|
||||
if (is_array($arguments) === false) {
|
||||
$arguments = [];
|
||||
}
|
||||
if (isset($arguments['events_search']) && is_array($arguments['events_search'])) {
|
||||
$arguments += $arguments['events_search'];
|
||||
unset($arguments['events_search']);
|
||||
}
|
||||
|
||||
// For legacy systems.
|
||||
$this->view->assignMultiple([
|
||||
'searchword' => $arguments['searchword'] ?? '',
|
||||
'selRegion' => $arguments['region'] ?? '',
|
||||
'start' => $arguments['start'] ?? '',
|
||||
'end' => $arguments['end'] ?? '',
|
||||
'considerDate' => $arguments['considerDate'] ?? '',
|
||||
]);
|
||||
|
||||
$demand = $this->demandFactory->fromSettings($this->settings);
|
||||
if ($search !== []) {
|
||||
$demand = DateDemand::createFromRequestValues($search, $this->settings);
|
||||
|
@ -178,17 +162,27 @@ class DateController extends AbstractController
|
|||
$this->view->assign('date', $date);
|
||||
}
|
||||
|
||||
protected function createDemandFromSearch(): DateDemand
|
||||
/**
|
||||
* Convert POST to proper GET.
|
||||
*
|
||||
* @see: https://en.wikipedia.org/wiki/Post/Redirect/Get
|
||||
*/
|
||||
private function handlePostRequest(): void
|
||||
{
|
||||
$arguments = $this->request->getArguments();
|
||||
if (isset($arguments['events_search'])) {
|
||||
$arguments += $arguments['events_search'];
|
||||
unset($arguments['events_search']);
|
||||
}
|
||||
|
||||
return DateDemand::createFromRequestValues(
|
||||
$arguments,
|
||||
$this->settings
|
||||
);
|
||||
if (
|
||||
$this->request->getMethod() === 'POST'
|
||||
&& $this->request->hasArgument('search')
|
||||
&& is_array($this->request->getArgument('search'))
|
||||
) {
|
||||
$namespace = $this->extensionService->getPluginNamespace(null, null);
|
||||
$this->redirectToUri($this->configurationManager->getContentObject()->typoLink_URL([
|
||||
'parameter' => 't3://page?uid=current',
|
||||
'additionalParams' => '&' . http_build_query([
|
||||
$namespace => [
|
||||
'search' => array_filter($this->request->getArgument('search'))
|
||||
],
|
||||
]),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -331,13 +331,18 @@ class DateDemand
|
|||
return $this->startObject;
|
||||
}
|
||||
|
||||
public function getStart(): ?int
|
||||
/**
|
||||
* Returns necessary format for forms.
|
||||
*
|
||||
* @internal Only for Extbase/Fluid.
|
||||
*/
|
||||
public function getStart(): string
|
||||
{
|
||||
if ($this->getStartObject() === null) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
return (int) $this->getStartObject()->format('U');
|
||||
return $this->getStartObject()->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function setStart(?int $start): void
|
||||
|
@ -362,13 +367,18 @@ class DateDemand
|
|||
return $this->getStartObject()->format('Y-m-d') === $this->getEndObject()->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function getEnd(): ?int
|
||||
/**
|
||||
* Returns necessary format for forms.
|
||||
*
|
||||
* @internal Only for Extbase/Fluid.
|
||||
*/
|
||||
public function getEnd(): string
|
||||
{
|
||||
if ($this->getEndObject() === null) {
|
||||
return null;
|
||||
return '';
|
||||
}
|
||||
|
||||
return (int) $this->getEndObject()->format('U');
|
||||
return $this->getEndObject()->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function setEnd(?int $end): void
|
||||
|
@ -387,15 +397,15 @@ class DateDemand
|
|||
|
||||
public function shouldShowFromNow(): bool
|
||||
{
|
||||
return $this->getStart() === null
|
||||
&& $this->getEnd() === null
|
||||
return $this->getStartObject() === null
|
||||
&& $this->getEndObject() === null
|
||||
&& $this->useMidnight === false;
|
||||
}
|
||||
|
||||
public function shouldShowFromMidnight(): bool
|
||||
{
|
||||
return $this->getStart() === null
|
||||
&& $this->getEnd() === null
|
||||
return $this->getStartObject() === null
|
||||
&& $this->getEndObject() === null
|
||||
&& $this->useMidnight === true;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,20 +77,20 @@ class DateRepository extends Repository
|
|||
$constraints['userCategories'] = $query->in('event.categories.uid', $demand->getUserCategories());
|
||||
}
|
||||
|
||||
if ($demand->getStart() !== null) {
|
||||
$constraints['starts'] = $query->greaterThanOrEqual('start', $demand->getStart());
|
||||
if ($demand->getStartObject() !== null) {
|
||||
$constraints['starts'] = $query->greaterThanOrEqual('start', $demand->getStartObject());
|
||||
}
|
||||
if ($demand->getEnd() != null) {
|
||||
if ($demand->getEndObject() != null) {
|
||||
// Dates might have end of 0 if only start exists.
|
||||
// This is respected to take start as end date.
|
||||
$constraints['ends'] = $query->logicalOr([
|
||||
$query->logicalAnd([
|
||||
$query->lessThanOrEqual('end', $demand->getEnd()),
|
||||
$query->lessThanOrEqual('end', $demand->getEndObject()),
|
||||
$query->greaterThan('end', 0)
|
||||
]),
|
||||
$query->logicalAnd([
|
||||
$query->equals('end', 0),
|
||||
$query->lessThanOrEqual('start', $demand->getEnd())
|
||||
$query->lessThanOrEqual('start', $demand->getEndObject())
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -23,14 +23,10 @@ plugin.tx_events {
|
|||
recursive = 1
|
||||
}
|
||||
features {
|
||||
#skipDefaultArguments = 1
|
||||
# if set to 1, the enable fields are ignored in BE context
|
||||
ignoreAllEnableFieldsInBe = 0
|
||||
# Should be on by default, but can be disabled if all action in the plugin are uncached
|
||||
requireCHashArgumentForActionArguments = 0
|
||||
skipDefaultArguments = 1
|
||||
}
|
||||
mvc {
|
||||
#callDefaultActionIfActionCantBeResolved = 1
|
||||
callDefaultActionIfActionCantBeResolved = 1
|
||||
}
|
||||
settings {
|
||||
|
||||
|
@ -74,4 +70,7 @@ plugin.tx_events {
|
|||
}
|
||||
}
|
||||
|
||||
plugin.tx_events_datelist.view.pluginNamespace = events_search
|
||||
plugin.tx_events_datesearch.view.pluginNamespace = events_search
|
||||
|
||||
module.tx_events < plugin.tx_events
|
||||
|
|
64
Documentation/Changelog/3.0.0.rst
Normal file
64
Documentation/Changelog/3.0.0.rst
Normal file
|
@ -0,0 +1,64 @@
|
|||
3.0.0
|
||||
=====
|
||||
|
||||
Breaking
|
||||
--------
|
||||
|
||||
Namespace changes
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
A new namespace was defined for plugins which is "events_search".
|
||||
The search parameters are now collected below namespace "search" instead of
|
||||
"events_search" leading to ``events_search[search][parametername]=value`` instead of
|
||||
``tx_events_signature[events_search][parametername]=value``.
|
||||
|
||||
The form now is submitted as post and redirects to a proper URL with GET.
|
||||
|
||||
The code was bloated and made it hard to fix bugs.
|
||||
|
||||
Necessary steps:
|
||||
|
||||
- Check usage of old namespace within templates and other sources.
|
||||
|
||||
- Check usage of old nesting of parameters.
|
||||
|
||||
API Changes
|
||||
^^^^^^^^^^^
|
||||
|
||||
The methods of ``DateDemand`` have changed, ``getStart()`` and ``getEnd()`` return a
|
||||
string value necessary or Fluid forms.
|
||||
Those are not considered public API. Use ``getStartObject()`` and ``getEndObject()``
|
||||
instead.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Nothing
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
* Keep filter during pagination
|
||||
|
||||
Search requests are POST by default.
|
||||
We apply PRG (=Post Redirect Get) on them to create proper GET requests.
|
||||
Those can be used to generate the URLs for pagination.
|
||||
|
||||
We follow Extbase, and do not explicitly ask for arguments from foreign namespaces.
|
||||
Instead we configure a pluginNamespace that's shared between plugins.
|
||||
This is all necessary as we still ship pre defined plugins.
|
||||
This should belong into integration of each project.
|
||||
|
||||
See: https://en.wikipedia.org/wiki/Post/Redirect/Get
|
||||
|
||||
Relates: #10175
|
||||
|
||||
Tasks
|
||||
-----
|
||||
|
||||
Nothing
|
||||
|
||||
Deprecation
|
||||
-----------
|
||||
|
||||
Nothing
|
|
@ -17,7 +17,7 @@
|
|||
</trans-unit>
|
||||
<trans-unit id="tx_events.searchform.date_to">
|
||||
<source>Date to</source>
|
||||
<target>Date bis</target>
|
||||
<target>Datum bis</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_events.searchform.regions">
|
||||
<source>All regions</source>
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
<f:if condition="{pagination.previousPageNumber} > 1">
|
||||
<f:then>
|
||||
<a class="page-link"
|
||||
href="{f:uri.action(arguments: {currentPage: pagination.previousPageNumber})}"
|
||||
href="{f:uri.action(addQueryString: 1, arguments: {currentPage: pagination.previousPageNumber})}"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<a class="page-link" href="{f:uri.action()}">
|
||||
<a class="page-link" href="{f:uri.action(addQueryString: 1)}">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</f:else>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<f:if condition="{pagination.displayRangeStart} > 1">
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="{f:uri.action()}"
|
||||
href="{f:uri.action(addQueryString: 1)}"
|
||||
aria-label="Goto Page 1"
|
||||
>
|
||||
1
|
||||
|
@ -54,7 +54,7 @@
|
|||
<f:if condition="{page} > 1">
|
||||
<f:then>
|
||||
<a class="page-link"
|
||||
href="{f:uri.action(arguments: {currentPage: page})}"
|
||||
href="{f:uri.action(addQueryString: 1, arguments: {currentPage: page})}"
|
||||
aria-label="Goto Page {page}"
|
||||
>
|
||||
{page}
|
||||
|
@ -62,7 +62,7 @@
|
|||
</f:then>
|
||||
<f:else>
|
||||
<a class="page-link"
|
||||
href="{f:uri.action()}"
|
||||
href="{f:uri.action(addQueryString: 1)}"
|
||||
aria-label="Goto Page 1"
|
||||
>
|
||||
1
|
||||
|
@ -83,7 +83,7 @@
|
|||
<f:if condition="{pagination.displayRangeEnd} < {pagination.lastPageNumber}">
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="{f:uri.action(arguments: {currentPage: pagination.lastPageNumber})}"
|
||||
href="{f:uri.action(addQueryString: 1, arguments: {currentPage: pagination.lastPageNumber})}"
|
||||
aria-label="Goto Page {pagination.lastPageNumber}"
|
||||
>
|
||||
{pagination.lastPageNumber}
|
||||
|
@ -94,7 +94,7 @@
|
|||
<f:if condition="{pagination.nextPageNumber}">
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
href="{f:uri.action(arguments: {currentPage: pagination.nextPageNumber})}"
|
||||
href="{f:uri.action(addQueryString: 1, arguments: {currentPage: pagination.nextPageNumber})}"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<f:section name="content">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-5">
|
||||
<f:form action="list" controller="Date" pluginName="DateList" method="get" id="events_search" name="events_search" object="{demand}">
|
||||
<f:form action="list" controller="Date" pluginName="DateList" method="post" id="events_search" name="search" object="{demand}">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-12 col-lg-6">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label for="searchword"><f:translate key="LLL:EXT:events/Resources/Private/Language/locallang.xlf:tx_events.searchform.searchword" /></label>
|
||||
<f:form.textfield type="text" class="form-control" id="searchword" name="searchword" value="{searchword}" />
|
||||
<f:form.textfield type="text" class="form-control" id="searchword" property="searchword" value="{searchword}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,23 +18,13 @@
|
|||
<div class="col col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="start"><f:translate key="LLL:EXT:events/Resources/Private/Language/locallang.xlf:tx_events.searchform.date_from" /></label>
|
||||
<div class="input-group date" id="date_start" data-target-input="nearest">
|
||||
<f:form.textfield type="text" class="form-control datetimepicker-input" id="start" name="start" value="{start}" additionalAttributes="{data-target: '#date_start'}" />
|
||||
<div class="input-group-append" data-target="#date_start" data-toggle="datetimepicker">
|
||||
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<f:form.textfield type="date" class="form-control" id="start" property="start" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="end"><f:translate key="LLL:EXT:events/Resources/Private/Language/locallang.xlf:tx_events.searchform.date_to" /></label>
|
||||
<div class="input-group date" id="date_end" data-target-input="nearest">
|
||||
<f:form.textfield type="text" class="form-control datetimepicker-input" id="start" name="end" value="{end}" additionalAttributes="{data-target: '#date_end'}" />
|
||||
<div class="input-group-append" data-target="#date_end" data-toggle="datetimepicker">
|
||||
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<f:form.textfield type="date" class="form-control" id="end" property="end" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,7 +34,7 @@
|
|||
<div class="row mt-3">
|
||||
<div class="col-4 col-md-4 col-lg-4">
|
||||
<div class="form-check">
|
||||
<f:form.radio class="form-check-input" name="region" value="{region.uid}" checked="{selRegion}==0" id="radio_0"/>
|
||||
<f:form.radio class="form-check-input" property="region" id="radio_0" value="" />
|
||||
<label class="form-check-label" for="radio_0"><f:translate key="LLL:EXT:events/Resources/Private/Language/locallang.xlf:tx_events.searchform.regions" /></label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,7 +42,7 @@
|
|||
<f:for each="{regions}" as="region">
|
||||
<div class="col-4">
|
||||
<div class="form-check">
|
||||
<f:form.radio class="form-check-input" name="region" value="{region.uid}" checked="{selRegion}=={region.uid}" id="radio_{region.uid}"/>
|
||||
<f:form.radio class="form-check-input" property="region" value="{region.uid}" id="radio_{region.uid}"/>
|
||||
<label class="form-check-label" for="radio_{region.uid}">{region.title}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -201,7 +201,7 @@ class DateDemandTest extends TestCase
|
|||
$result->getStartObject()->format('Y-m-d')
|
||||
);
|
||||
self::assertSame(
|
||||
1657576800,
|
||||
'2022-07-12',
|
||||
$result->getStart()
|
||||
);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ class DateDemandTest extends TestCase
|
|||
$result->getEndObject()->format('Y-m-d')
|
||||
);
|
||||
self::assertSame(
|
||||
1657663140,
|
||||
'2022-07-12',
|
||||
$result->getEnd()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ $EM_CONF['events'] = [
|
|||
'state' => 'alpha',
|
||||
'createDirs' => '',
|
||||
'clearCacheOnLoad' => 0,
|
||||
'version' => '2.6.3',
|
||||
'version' => '3.0.0',
|
||||
'constraints' => [
|
||||
'depends' => [
|
||||
'typo3' => '10.4.00-11.5.99',
|
||||
|
|
|
@ -37,6 +37,8 @@ call_user_func(function () {
|
|||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['events_category'] = [];
|
||||
}
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = '^events_search';
|
||||
|
||||
$iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
|
||||
$iconRegistry->registerIcon(
|
||||
'events-plugin',
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Cannot call method typoLink_URL\\(\\) on TYPO3\\\\CMS\\\\Frontend\\\\ContentObject\\\\ContentObjectRenderer\\|null\\.$#"
|
||||
count: 1
|
||||
path: Classes/Controller/DateController.php
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$categories of method Wrm\\\\Events\\\\Domain\\\\Model\\\\Event\\:\\:setCategories\\(\\) expects TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\<Wrm\\\\Events\\\\Domain\\\\Model\\\\Category\\>, TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\<mixed\\> given\\.$#"
|
||||
count: 1
|
||||
|
|
Loading…
Reference in a new issue