Add pages to events

Allow editor to reference TYPO3 page records from events.
E.g. used to provide further internal information links.

Also those pages are resolved via DataProcessing.
That allows integrators to be 100% flexible to resolve those page info.
By default a menu is generated and media is resolved.

Also validation is ignored, as this improves performance and allows to
use the new class as injected property without validation fails.
Models are not managed via frontend and therefore don't need validation.

Relates: #8092
This commit is contained in:
Daniel Siepmann 2021-01-11 09:20:51 +01:00
parent acc0c2bdc5
commit 38b4844a9d
9 changed files with 495 additions and 221 deletions

View file

@ -4,6 +4,7 @@ namespace Wrm\Events\Controller;
use TYPO3\CMS\Core\Database\QueryGenerator; use TYPO3\CMS\Core\Database\QueryGenerator;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use Wrm\Events\Domain\Model\Date; use Wrm\Events\Domain\Model\Date;
@ -11,6 +12,7 @@ use Wrm\Events\Domain\Model\Dto\DateDemand;
use Wrm\Events\Domain\Repository\CategoryRepository; use Wrm\Events\Domain\Repository\CategoryRepository;
use Wrm\Events\Domain\Repository\DateRepository; use Wrm\Events\Domain\Repository\DateRepository;
use Wrm\Events\Domain\Repository\RegionRepository; use Wrm\Events\Domain\Repository\RegionRepository;
use Wrm\Events\Service\DataProcessingForModels;
/** /**
* DateController * DateController
@ -38,6 +40,11 @@ class DateController extends ActionController
*/ */
protected $queryGenerator; protected $queryGenerator;
/**
* @var DataProcessingForModels
*/
protected $dataProcessing;
/** /**
* @var array * @var array
*/ */
@ -58,11 +65,20 @@ class DateController extends ActionController
$this->categoryRepository = $categoryRepository; $this->categoryRepository = $categoryRepository;
} }
/**
* @param DataProcessingForModels $dataProcessing
*/
public function injectDataProcessingForModels(DataProcessingForModels $dataProcessing)
{
$this->dataProcessing = $dataProcessing;
}
/** /**
* Action initializer * Action initializer
*/ */
protected function initializeAction() protected function initializeAction()
{ {
$this->dataProcessing->setConfigurationManager($this->configurationManager);
$this->pluginSettings = $this->configurationManager->getConfiguration( $this->pluginSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
); );
@ -126,6 +142,8 @@ class DateController extends ActionController
/** /**
* action show * action show
* *
* @Extbase\IgnoreValidation("date")
*
* @param \Wrm\Events\Domain\Model\Date $date * @param \Wrm\Events\Domain\Model\Date $date
* @return void * @return void
*/ */

View file

@ -3,11 +3,13 @@
namespace Wrm\Events\Controller; namespace Wrm\Events\Controller;
use TYPO3\CMS\Core\Database\QueryGenerator; use TYPO3\CMS\Core\Database\QueryGenerator;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use Wrm\Events\Domain\Model\Dto\EventDemand; use Wrm\Events\Domain\Model\Dto\EventDemand;
use Wrm\Events\Domain\Model\Event; use Wrm\Events\Domain\Model\Event;
use Wrm\Events\Domain\Repository\EventRepository; use Wrm\Events\Domain\Repository\EventRepository;
use Wrm\Events\Service\DataProcessingForModels;
/** /**
* EventController * EventController
@ -26,6 +28,11 @@ class EventController extends ActionController
*/ */
protected $queryGenerator; protected $queryGenerator;
/**
* @var DataProcessingForModels
*/
protected $dataProcessing;
/** /**
* @var array * @var array
*/ */
@ -39,11 +46,20 @@ class EventController extends ActionController
$this->eventRepository = $eventRepository; $this->eventRepository = $eventRepository;
} }
/**
* @param DataProcessingForModels $dataProcessing
*/
public function injectDataProcessingForModels(DataProcessingForModels $dataProcessing)
{
$this->dataProcessing = $dataProcessing;
}
/** /**
* Action initializer * Action initializer
*/ */
protected function initializeAction() protected function initializeAction()
{ {
$this->dataProcessing->setConfigurationManager($this->configurationManager);
$this->pluginSettings = $this->configurationManager->getConfiguration( $this->pluginSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
); );
@ -65,6 +81,8 @@ class EventController extends ActionController
/** /**
* Action show * Action show
* *
* @Extbase\IgnoreValidation("event")
*
* @param Event $event * @param Event $event
* @return void * @return void
*/ */

View file

@ -2,7 +2,6 @@
namespace Wrm\Events\Domain\Model; namespace Wrm\Events\Domain\Model;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation as Extbase; use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Domain\Model\Category; use TYPO3\CMS\Extbase\Domain\Model\Category;
@ -10,6 +9,7 @@ use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage; use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use Wrm\Events\Domain\Repository\DateRepository; use Wrm\Events\Domain\Repository\DateRepository;
use Wrm\Events\Service\DataProcessingForModels;
/** /**
* Event * Event
@ -194,6 +194,11 @@ class Event extends AbstractEntity
*/ */
protected $region = null; protected $region = null;
/**
* @var string
*/
protected $pages = '';
/** /**
* categories * categories
* *
@ -206,6 +211,11 @@ class Event extends AbstractEntity
*/ */
protected $_languageUid; protected $_languageUid;
/**
* @var DataProcessingForModels
*/
private $dataProcessing = null;
/** /**
* __construct * __construct
*/ */
@ -216,6 +226,14 @@ class Event extends AbstractEntity
$this->initStorageObjects(); $this->initStorageObjects();
} }
/**
* @param DataProcessingForModels $dataProcessing
*/
public function injectDataProcessingForModels(DataProcessingForModels $dataProcessing)
{
$this->dataProcessing = $dataProcessing;
}
/** /**
* @return void * @return void
*/ */
@ -688,6 +706,18 @@ class Event extends AbstractEntity
$this->country = $country; $this->country = $country;
} }
public function getPages(): array
{
static $pages = null;
if (is_array($pages)) {
return $pages;
}
$pages = $this->dataProcessing->process($this);
return $pages;
}
/** /**
* @param \TYPO3\CMS\Extbase\Domain\Model\Category<\TYPO3\CMS\Extbase\Domain\Model\Category> $category * @param \TYPO3\CMS\Extbase\Domain\Model\Category<\TYPO3\CMS\Extbase\Domain\Model\Category> $category
*/ */

View file

@ -0,0 +1,176 @@
<?php
namespace Wrm\Events\Service;
/*
* Copyright (C) 2021 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program 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.
*
* This program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory;
use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Used by models to apply data processing.
* This allows for flexibility of integrators.
*
* E.g. pages are saved for each event.
* An integrator now can resolve them via data processing to arrays or menus.
*
* dataProcessing is configured via TypoScript for each plugin or whole extension via
* settings.dataProcessing.fqcn, e.g.:
*
* plugin.tx_events {
* settings {
* dataProcessing {
* Wrm\Events\Domain\Model\Event {
* 10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
* 10 {
* special = list
* special.value.field = pages
* dataProcessing {
* 10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
* 10 {
* references.fieldName = media
* }
* }
* }
* }
* }
* }
* }
*
* Currently supported by:
*
* - Event->getPages()
*/
class DataProcessingForModels implements SingletonInterface
{
/**
* @var ContentObjectRenderer
*/
private $cObject;
/**
* @var ContentDataProcessor
*/
private $processorHandler;
/**
* @var Connection
*/
private $connection;
/**
* @var DataMapFactory
*/
private $dataMapFactory;
/**
* @var ConfigurationManagerInterface|null
*/
private $configurationManager;
/**
* @var TypoScriptService
*/
private $typoScriptService;
public function __construct(
ContentDataProcessor $processorHandler,
ConnectionPool $connectionPool,
DataMapFactory $dataMapFactory,
TypoScriptService $typoScriptService
) {
$this->cObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$this->processorHandler = $processorHandler;
$this->connection = $connectionPool->getConnectionByName('Default');
$this->dataMapFactory = $dataMapFactory;
$this->typoScriptService = $typoScriptService;
}
/**
* Used to set current configuration from within plugin.
*
* Inject and call this method, e.g. within initializeAction.
* Necessary to get plugin configuration containing dataProcessing configuration.
*/
public function setConfigurationManager(ConfigurationManagerInterface $configurationManager): void
{
$this->configurationManager = $configurationManager;
}
public function process(
AbstractEntity $entity
): array {
$configuration = $this->getConfiguration($entity);
if ($configuration === []) {
return [];
}
$this->cObject->start($this->getData($entity), $this->getTable($entity));
return $this->processorHandler->process($this->cObject, $configuration, []);
}
private function getData(AbstractEntity $entity): array
{
$row = $this->connection->select(['*'], $this->getTable($entity), ['uid' => $entity->getUid()])->fetch();
if (is_array($row)) {
return $row;
}
return [];
}
private function getTable(AbstractEntity $entity): string
{
$dataMap = $this->dataMapFactory->buildDataMap(get_class($entity));
return $dataMap->getTableName();
}
private function getConfiguration(AbstractEntity $entity): array
{
if ($this->configurationManager === null) {
return [];
}
$className = get_class($entity);
$settings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
);
if (ArrayUtility::isValidPath($settings, 'dataProcessing.' . $className, '.') === false) {
return [];
}
$configuration = ArrayUtility::getValueByPath($settings, 'dataProcessing.' . $className, '.');
$configuration = $this->typoScriptService->convertPlainArrayToTypoScriptArray($configuration);
return [
'dataProcessing.' => $configuration,
];
}
}

View file

@ -20,7 +20,7 @@ return [
'iconfile' => 'EXT:events/Resources/Public/Icons/tx_events_domain_model_event.gif' 'iconfile' => 'EXT:events/Resources/Public/Icons/tx_events_domain_model_event.gif'
], ],
'types' => [ 'types' => [
'1' => ['showitem' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, title, global_id, slug, highlight, teaser, details, price_info, name, street, district, city, zip, country, phone, web, ticket, facebook, youtube, instagram, latitude, longitude, images, categories, dates, organizer, region, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, starttime, endtime'], '1' => ['showitem' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, title, global_id, slug, highlight, teaser, details, price_info, name, street, district, city, zip, country, phone, web, ticket, facebook, youtube, instagram, latitude, longitude, images, pages, categories, dates, organizer, region, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, starttime, endtime'],
], ],
'columns' => [ 'columns' => [
'sys_language_uid' => [ 'sys_language_uid' => [
@ -379,6 +379,16 @@ return [
), ),
], ],
'pages' => [
'exclude' => true,
'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_event.xlf:tx_events_domain_model_event.pages',
'config' => [
'type' => 'group',
'internal_type' => 'db',
'allowed' => 'pages',
],
],
'categories' => [ 'categories' => [
'exclude' => true, 'exclude' => true,
'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_event.xlf:tx_events_domain_model_event.categories', 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_event.xlf:tx_events_domain_model_event.categories',
@ -443,6 +453,5 @@ return [
'maxitems' => 1, 'maxitems' => 1,
], ],
], ],
], ],
]; ];

View file

@ -56,6 +56,21 @@ plugin.tx_events {
categoriesPid = {$plugin.tx_events.settings.destinationData.categoriesPid} categoriesPid = {$plugin.tx_events.settings.destinationData.categoriesPid}
categoryParentUid = {$plugin.tx_events.settings.destinationData.categoryParentUid} categoryParentUid = {$plugin.tx_events.settings.destinationData.categoryParentUid}
} }
dataProcessing {
Wrm\Events\Domain\Model\Event {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
special = list
special.value.field = pages
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
10 {
references.fieldName = media
}
}
}
}
}
} }
} }

View file

@ -103,6 +103,10 @@
<source>Images</source> <source>Images</source>
<target>Bilder</target> <target>Bilder</target>
</trans-unit> </trans-unit>
<trans-unit id="tx_events_domain_model_event.pages">
<source>Pages</source>
<target>Seiten</target>
</trans-unit>
<trans-unit id="tx_events_domain_model_event.categories"> <trans-unit id="tx_events_domain_model_event.categories">
<source>Categories</source> <source>Categories</source>
<target>Kategorien</target> <target>Kategorien</target>

View file

@ -78,6 +78,9 @@
<trans-unit id="tx_events_domain_model_event.images"> <trans-unit id="tx_events_domain_model_event.images">
<source>Images</source> <source>Images</source>
</trans-unit> </trans-unit>
<trans-unit id="tx_events_domain_model_event.pages">
<source>Pages</source>
</trans-unit>
<trans-unit id="tx_events_domain_model_event.categories"> <trans-unit id="tx_events_domain_model_event.categories">
<source>Categories</source> <source>Categories</source>
</trans-unit> </trans-unit>

View file

@ -26,6 +26,7 @@ CREATE TABLE tx_events_domain_model_event (
longitude varchar(255) DEFAULT '' NOT NULL, longitude varchar(255) DEFAULT '' NOT NULL,
images int(11) unsigned NOT NULL default '0', images int(11) unsigned NOT NULL default '0',
categories int(11) DEFAULT '0' NOT NULL, categories int(11) DEFAULT '0' NOT NULL,
pages text,
dates int(11) unsigned DEFAULT '0' NOT NULL, dates int(11) unsigned DEFAULT '0' NOT NULL,
organizer int(11) unsigned DEFAULT '0', organizer int(11) unsigned DEFAULT '0',
region int(11) unsigned DEFAULT '0', region int(11) unsigned DEFAULT '0',