Add PSR-14 Event to modify event objects prior persistence (#35)

A new PSR-14 event is added that allows to modify the event right before
it is persisted within Destination Data One import.

This for example allows to alter dates, prices, etc.

Improve handling of missing end time in dates.
This commit is contained in:
Daniel Siepmann 2023-08-10 14:20:37 +02:00 committed by GitHub
parent 05f3ec4fa7
commit 3d0d5d8645
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 491 additions and 18 deletions

View file

@ -15,7 +15,7 @@ class Date extends AbstractEntity
protected $start; protected $start;
/** /**
* @var \DateTime * @var \DateTime|null
*/ */
protected $end; protected $end;
@ -73,7 +73,7 @@ class Date extends AbstractEntity
} }
/** /**
* @return \DateTime end * @return \DateTime|null end
*/ */
public function getEnd() public function getEnd()
{ {
@ -81,23 +81,25 @@ class Date extends AbstractEntity
} }
/** /**
* @param \DateTime $end * @param \DateTime|null $end
* *
* @return void * @return void
*/ */
public function setEnd(\DateTime $end) public function setEnd($end)
{ {
$this->end = $end; $this->end = $end;
} }
public function getHasUsefulEndTime(): bool public function getHasUsefulEndTime(): bool
{ {
return $this->getEnd()->format('H:i') !== '23:59'; $end = $this->getEnd();
return $end && $end->format('H:i') !== '23:59';
} }
public function getEndsOnSameDay(): bool public function getEndsOnSameDay(): bool
{ {
return $this->getStart()->format('Y-m-d') === $this->getEnd()->format('Y-m-d'); $end = $this->getEnd();
return $end && $this->getStart()->format('Y-m-d') === $end->format('Y-m-d');
} }
/** /**

View file

@ -3,6 +3,7 @@
namespace Wrm\Events\Service; namespace Wrm\Events\Service;
use Exception; use Exception;
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
use TYPO3\CMS\Core\Log\Logger; use TYPO3\CMS\Core\Log\Logger;
use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
@ -22,6 +23,7 @@ use Wrm\Events\Service\DestinationDataImportService\CategoriesAssignment;
use Wrm\Events\Service\DestinationDataImportService\CategoriesAssignment\Import as CategoryImport; use Wrm\Events\Service\DestinationDataImportService\CategoriesAssignment\Import as CategoryImport;
use Wrm\Events\Service\DestinationDataImportService\DataFetcher; use Wrm\Events\Service\DestinationDataImportService\DataFetcher;
use Wrm\Events\Service\DestinationDataImportService\DatesFactory; use Wrm\Events\Service\DestinationDataImportService\DatesFactory;
use Wrm\Events\Service\DestinationDataImportService\Events\EventImportEvent;
use Wrm\Events\Service\DestinationDataImportService\FilesAssignment; use Wrm\Events\Service\DestinationDataImportService\FilesAssignment;
use Wrm\Events\Service\DestinationDataImportService\LocationAssignment; use Wrm\Events\Service\DestinationDataImportService\LocationAssignment;
use Wrm\Events\Service\DestinationDataImportService\Slugger; use Wrm\Events\Service\DestinationDataImportService\Slugger;
@ -108,6 +110,11 @@ class DestinationDataImportService
*/ */
private $cacheManager; private $cacheManager;
/**
* @var EventDispatcher
*/
private $eventDispatcher;
/** /**
* ImportService constructor. * ImportService constructor.
* *
@ -123,6 +130,7 @@ class DestinationDataImportService
* @param LocationAssignment $locationAssignment * @param LocationAssignment $locationAssignment
* @param Slugger $slugger * @param Slugger $slugger
* @param CacheManager $cacheManager * @param CacheManager $cacheManager
* @param EventDispatcher $eventDispatcher
*/ */
public function __construct( public function __construct(
EventRepository $eventRepository, EventRepository $eventRepository,
@ -137,7 +145,8 @@ class DestinationDataImportService
CategoriesAssignment $categoriesAssignment, CategoriesAssignment $categoriesAssignment,
LocationAssignment $locationAssignment, LocationAssignment $locationAssignment,
Slugger $slugger, Slugger $slugger,
CacheManager $cacheManager CacheManager $cacheManager,
EventDispatcher $eventDispatcher
) { ) {
$this->eventRepository = $eventRepository; $this->eventRepository = $eventRepository;
$this->organizerRepository = $organizerRepository; $this->organizerRepository = $organizerRepository;
@ -152,6 +161,7 @@ class DestinationDataImportService
$this->locationAssignment = $locationAssignment; $this->locationAssignment = $locationAssignment;
$this->slugger = $slugger; $this->slugger = $slugger;
$this->cacheManager = $cacheManager; $this->cacheManager = $cacheManager;
$this->eventDispatcher = $eventDispatcher;
} }
public function import( public function import(
@ -198,6 +208,7 @@ class DestinationDataImportService
// Event already exists? If not create one! // Event already exists? If not create one!
$this->tmpCurrentEvent = $this->getOrCreateEvent($event['global_id'], $event['title']); $this->tmpCurrentEvent = $this->getOrCreateEvent($event['global_id'], $event['title']);
$existingEvent = clone $this->tmpCurrentEvent;
// Set language UID // Set language UID
$this->tmpCurrentEvent->setLanguageUid(-1); $this->tmpCurrentEvent->setLanguageUid(-1);
@ -266,6 +277,11 @@ class DestinationDataImportService
$this->setAssets($event['media_objects']); $this->setAssets($event['media_objects']);
} }
$this->eventDispatcher->dispatch(new EventImportEvent(
$existingEvent,
$this->tmpCurrentEvent
));
// Update and persist // Update and persist
$this->logger->info('Persist database'); $this->logger->info('Persist database');
$this->eventRepository->update($this->tmpCurrentEvent); $this->eventRepository->update($this->tmpCurrentEvent);

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 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.
*/
namespace Wrm\Events\Service\DestinationDataImportService\Events;
use Wrm\Events\Domain\Model\Event;
final class EventImportEvent
{
/**
* @var Event
*/
private $existingEvent;
/**
* @var Event
*/
private $eventToImport;
public function __construct(
Event $existingEvent,
Event $eventToImport
) {
$this->existingEvent = $existingEvent;
$this->eventToImport = $eventToImport;
}
public function getBaseEvent(): Event
{
return clone $this->existingEvent;
}
public function getEventToImport(): Event
{
return $this->eventToImport;
}
}

View file

@ -148,7 +148,7 @@ final class MigrateDuplicateLocations implements UpgradeWizardInterface
return 0; return 0;
} }
return (int) $uid; return (int)$uid;
} }
private function buildObject(array $location): Location private function buildObject(array $location): Location

View file

@ -0,0 +1,38 @@
3.5.0
=====
Breaking
--------
Nothing
Features
--------
* Add PSR-14 Events to Destination Data One import.
They allow individual installation to alter the import.
See :ref:`psr14` for an overview of PSR-14 Events.
Added Events:
* Allow to modify an event object before importing.
Fixes
-----
* Improve handling of dates with no end.
This was always technically possible. Still support from extension was missing.
This now got improved, the date object will not throw exceptions due to ``format()`` calls on none.
Furthermore the PHPDoc now reflects that there might be null instead of ``\DateTime``.
Also the setter was adjusted to allow ``null`` values.
Tasks
-----
Nothing
Deprecation
-----------
Nothing

View file

@ -0,0 +1,13 @@
.. index:: single: PSR-14 Events
.. _psr14:
PSR-14 Events
=============
.. index:: single: PSR-14 Events; Destination Data One Import: Event Import
Destination Data One Import: ``EventImportEvent``
-------------------------------------------------
Executed during Destination Data One Import.
Allows to alter the event prior persistence.

View file

@ -15,11 +15,11 @@ tx_events_domain_model_event
tx_events_domain_model_date tx_events_domain_model_date
,uid,pid,event,start,end ,uid,pid,event,start,end
,1,2,1,1676419200,1676484000 ,1,2,1,1676419200,1676484000
,2,2,2,1676419200,0 ,2,2,2,1676419200,"\NULL"
,3,2,3,1676678400,1676743200 ,3,2,3,1676678400,1676743200
,4,2,4,1676678400,0 ,4,2,4,1676678400,"\NULL"
,5,2,5,1676419200,1676678400 ,5,2,5,1676419200,1676678400
,6,2,6,1676559600,1676570400 ,6,2,6,1676559600,1676570400
,7,2,7,1676559600,1676678400 ,7,2,7,1676559600,1676678400
,8,2,8,1676559600,0 ,8,2,8,1676559600,"\NULL"
,9,2,9,1676419200,1676570400 ,9,2,9,1676419200,1676570400

Can't render this file because it has a wrong number of fields in line 2.

View file

@ -8,9 +8,7 @@ abstract class AbstractTest extends AbstractFunctionalTestCase
{ {
protected function setUp(): void protected function setUp(): void
{ {
$this->coreExtensionsToLoad = [ $this->coreExtensionsToLoad[] = 'filemetadata';
'filemetadata',
];
parent::setUp(); parent::setUp();

View file

@ -0,0 +1,29 @@
<?php
return [
'tx_events_domain_model_event' => [
[
'uid' => 1,
'pid' => 2,
'title' => 'Event for modifying event',
'global_id' => 'e_100350503',
'dates' => 2,
],
],
'tx_events_domain_model_date' => [
[
'uid' => 1,
'pid' => 2,
'event' => 1,
'start' => 4097728800,
'end' => null,
],
[
'uid' => 2,
'pid' => 2,
'event' => 1,
'start' => 4097815200,
'end' => null,
],
],
];

View file

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 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.
*/
namespace Wrm\Events\Tests\Functional\Psr14Events\DestinationDataImport;
use GuzzleHttp\Psr7\Response;
use Wrm\Events\Tests\Functional\Import\DestinationDataTest\AbstractTest;
final class EventImportEventTest extends AbstractTest
{
protected function setUp(): void
{
$this->testExtensionsToLoad[] = 'typo3conf/ext/events/Tests/Functional/Psr14Events/DestinationDataImport/Fixtures/Extensions/custom_event';
parent::setUp();
$this->setUpConfiguration([
'restUrl = https://example.com/some-path/',
'license = example-license',
'restType = Event',
'restLimit = 3',
'restMode = next_months,12',
'restTemplate = ET2014A.json',
]);
}
/**
* @test
*/
public function registeredEventHandlerCanModifyEvent(): void
{
$this->importPHPDataSet(__DIR__ . '/Fixtures/Database/RegisteredEventHandlerCanModifyEvent.php');
$this->setUpResponses([new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/Responses/RegisteredEventHandlerCanModifyEvent.json') ?: '')]);
$this->executeCommand();
$this->assertPHPDataSet(__DIR__ . '/Assertions/RegisteredEventHandlerCanModifyEvent.php');
}
}

View file

@ -0,0 +1,15 @@
<?php
return [
'tx_events_domain_model_import' => [
[
'uid' => '1',
'pid' => '2',
'title' => 'Example import configuration',
'storage_pid' => 2,
'files_folder' => '1:/staedte/beispielstadt/events/',
'region' => '1',
'rest_experience' => 'beispielstadt',
],
],
];

View file

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 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.
*/
namespace WerkraumMedia\CustomEvent\EventListener;
use Wrm\Events\Service\DestinationDataImportService\Events\EventImportEvent;
final class EventImportListener
{
public function __invoke(EventImportEvent $psr14Event): void
{
$eventToImport = $psr14Event->getEventToImport();
$dates = $psr14Event->getEventToImport()->getDates();
foreach ($dates as $date) {
$endToSkip = clone $date->getStart();
$endToSkip->modify('+15 minutes');
if ($date->getEnd() == $endToSkip) {
$date->setEnd(null);
}
}
$eventToImport->setDates($dates);
}
}

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use WerkraumMedia\CustomEvent\EventListener\EventImportListener;
use Wrm\Events\Service\DestinationDataImportService\Events\EventImportEvent;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator->services()
->defaults()
->autowire()
->autoconfigure()
;
$services->load('WerkraumMedia\\CustomEvent\\', '../Classes/*');
$services->set(EventImportListener::class)
->tag('event.listener', [
'event' => EventImportEvent::class,
])
;
};

View file

@ -0,0 +1,19 @@
{
"name": "werkraummedia/custom_event",
"description": "Integrates custom event specifics",
"type": "typo3-cms-extension",
"license": "GPL-2.0-or-later",
"require": {
"werkraummedia/events": "*"
},
"autoload": {
"psr-4": {
"WerkraumMedia\\CustomEvent\\": "Classes/"
}
},
"extra": {
"typo3/cms": {
"extension-key": "custom_event"
}
}
}

View file

@ -0,0 +1,20 @@
<?php
$EM_CONF['custom_event'] = [
'title' => 'Custom Events',
'description' => 'Integrates custom event specifics',
'category' => 'plugin',
'author' => 'Daniel Siepmann',
'author_email' => 'coding@daniel-siepmann.de',
'state' => 'alpha',
'createDirs' => '',
'clearCacheOnLoad' => 0,
'version' => '1.0.0',
'constraints' => [
'depends' => [
'event' => '',
],
'conflicts' => [],
'suggests' => [],
],
];

View file

@ -0,0 +1,94 @@
{
"status": "OK",
"count": 1,
"overallcount": 1,
"channels": [],
"facetGroups": [],
"items": [
{
"global_id": "e_100350503",
"id": "100350503",
"title": "Event for modifying event",
"type": "Event",
"categories": [
],
"features": [
],
"texts": [
{
"rel": "details",
"type": "text/html",
"value": "Immer mittwochs in der Adventszeit spielt Frank Bettenhausen solo und zusammen mit anderen Musikern auf der Steinmeyerorgel aus dem Jahr 1906.&nbsp;&nbsp;Bekannte Adventslieder, barocke und romantische Kompositionen stehen neben besinnlichen Texten von Pfarrer Johannes-Martin Weiss.<br><br><strong>Es gilt die 2G-PLUS-Regel.</strong><br>"
},
{
"rel": "details",
"type": "text/plain",
"value": "Immer mittwochs in der Adventszeit spielt Frank Bettenhausen solo und zusammen mit anderen Musikern auf der Steinmeyerorgel aus dem Jahr 1906. Bekannte Adventslieder, barocke und romantische Kompositionen stehen neben besinnlichen Texten von Pfarrer Johannes-Martin Weiss.\n\nEs gilt die 2G-PLUS-Regel."
},
{
"rel": "teaser",
"type": "text/html"
},
{
"rel": "teaser",
"type": "text/plain"
}
],
"city": "Rudolstadt",
"zip": "07407",
"street": "Caspar-Schulte-Straße",
"phone": "03672 - 48 96 13",
"author": "support@hubermedia.de",
"media_objects": [
],
"keywords": [],
"timeIntervals": [
{
"weekdays": [
"Saturday",
"Sunday"
],
"start": "2099-11-02T11:00:00+01:00",
"end": "2099-11-02T11:15:00+01:00",
"repeatUntil": "2099-11-10T11:15:00+01:00",
"tz": "Europe/Berlin",
"freq": "Weekly",
"interval": 1
}
],
"name": "Lutherkirche",
"addresses": [
{
"name": "Städtetourismus in Thüringen e.V.",
"city": "Weimar",
"zip": "99423",
"street": "UNESCO-Platz 1",
"phone": "+49 (3643) 745 314",
"web": "http://www.thueringer-staedte.de",
"email": "verein@thueringer-staedte.de",
"rel": "author"
},
{
"name": "Städtetourismus in Thüringen\" e.V.",
"web": "http://www.thueringer-staedte.de",
"email": "verein@thueringer-staedte.de",
"rel": "organisation"
},
{
"name": "Lutherkirche",
"city": "Rudolstadt",
"zip": "07407",
"street": "Caspar-Schulte-Straße",
"phone": "03672 - 48 96 13",
"rel": "organizer"
}
],
"created": "2099-11-08T22:15:00+00:00",
"changed": "2099-12-14T08:38:00+00:00",
"source": {
"url": "http://destination.one/",
"value": "destination.one"
}
}
]
}

View file

@ -59,7 +59,7 @@ class DateTest extends TestCase
/** /**
* @test * @test
*/ */
public function returnsThatItDoesNotHaveUsefulEndTime(): void public function returnsThatItDoesNotHaveUsefulEndTimeWithTime(): void
{ {
$subject = new Date(); $subject = new Date();
$subject->setEnd(new \DateTime('2022-07-11T23:59:00')); $subject->setEnd(new \DateTime('2022-07-11T23:59:00'));
@ -67,6 +67,17 @@ class DateTest extends TestCase
self::assertFalse($subject->getHasUsefulEndTime()); self::assertFalse($subject->getHasUsefulEndTime());
} }
/**
* @test
*/
public function returnsThatItDoesNotHaveUsefulEndTimeWithNull(): void
{
$subject = new Date();
$subject->setEnd(null);
self::assertFalse($subject->getHasUsefulEndTime());
}
/** /**
* @test * @test
*/ */
@ -82,7 +93,7 @@ class DateTest extends TestCase
/** /**
* @test * @test
*/ */
public function returnsThatItDoesNotEndOnSameDay(): void public function returnsThatItDoesNotEndOnSameDayWithDifferentDates(): void
{ {
$subject = new Date(); $subject = new Date();
$subject->setStart(new \DateTime('2022-07-11T14:00:00')); $subject->setStart(new \DateTime('2022-07-11T14:00:00'));
@ -90,4 +101,39 @@ class DateTest extends TestCase
self::assertFalse($subject->getEndsOnSameDay()); self::assertFalse($subject->getEndsOnSameDay());
} }
/**
* @test
*/
public function returnsThatItDoesNotEndOnSameDayWithMissingEnd(): void
{
$subject = new Date();
$subject->setStart(new \DateTime('2022-07-11T14:00:00'));
$subject->setEnd(null);
self::assertFalse($subject->getEndsOnSameDay());
}
/**
* @test
*/
public function returnsNullAsEnd(): void
{
$subject = new Date();
$subject->setEnd(null);
self::assertNull($subject->getEnd());
}
/**
* @test
*/
public function returnsEnd(): void
{
$end = new \DateTime('2022-07-13T22:00:00');
$subject = new Date();
$subject->setEnd($end);
self::assertSame($end, $subject->getEnd());
}
} }

View file

@ -30,7 +30,8 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Wrm\\Events\\Tests\\": "Tests" "Wrm\\Events\\Tests\\": "Tests",
"WerkraumMedia\\CustomEvent\\": "Tests/Functional/Psr14Events/DestinationDataImport/Fixtures/Extensions/custom_event/Classes/"
} }
}, },
"scripts": { "scripts": {

View file

@ -9,7 +9,7 @@ $EM_CONF['events'] = [
'state' => 'alpha', 'state' => 'alpha',
'createDirs' => '', 'createDirs' => '',
'clearCacheOnLoad' => 0, 'clearCacheOnLoad' => 0,
'version' => '3.4.0', 'version' => '3.5.0',
'constraints' => [ 'constraints' => [
'depends' => [ 'depends' => [
'typo3' => '10.4.00-11.5.99', 'typo3' => '10.4.00-11.5.99',