Add meta tags (#49)

A new class is added which will add meta tags for dates and events.
The class has an interface which allows it to be replaced via DI to
alter behaviour.

Refactor import regarding data handler. We now also need to add a new
column "keywords". We use the new DataHandler approach.
But that approach only covered relations so far. We therefore refactor
that area to be more generic and use that one for new keywords column.

Relates: #10642
This commit is contained in:
Daniel Siepmann 2023-11-29 10:36:59 +01:00
parent 275dc3ffc1
commit 1e98df689b
24 changed files with 708 additions and 59 deletions

View file

@ -13,6 +13,7 @@ use Wrm\Events\Domain\Repository\DateRepository;
use Wrm\Events\Domain\Repository\RegionRepository;
use Wrm\Events\Events\Controller\DateListVariables;
use Wrm\Events\Events\Controller\DateSearchVariables;
use Wrm\Events\Frontend\MetaInformation\DateMetaInformationInterface;
use Wrm\Events\Pagination\Factory;
use Wrm\Events\Service\DataProcessingForModels;
@ -58,6 +59,11 @@ class DateController extends AbstractController
*/
protected $extensionService;
/**
* @var DateMetaInformationInterface
*/
protected $metaInformationService;
public function __construct(
DateDemandFactory $demandFactory,
DateRepository $dateRepository,
@ -66,7 +72,8 @@ class DateController extends AbstractController
Factory $paginationFactory,
DataProcessingForModels $dataProcessing,
EventDispatcher $eventDispatcher,
ExtensionService $extensionService
ExtensionService $extensionService,
DateMetaInformationInterface $metaInformationService
) {
$this->demandFactory = $demandFactory;
$this->dateRepository = $dateRepository;
@ -76,6 +83,7 @@ class DateController extends AbstractController
$this->dataProcessing = $dataProcessing;
$this->eventDispatcher = $eventDispatcher;
$this->extensionService = $extensionService;
$this->metaInformationService = $metaInformationService;
}
protected function initializeAction(): void
@ -163,6 +171,7 @@ class DateController extends AbstractController
$this->trigger404('No event found for requested date.');
}
$this->metaInformationService->setDate($date);
$this->view->assign('date', $date);
}

View file

@ -3,6 +3,7 @@
namespace Wrm\Events\Controller;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use Wrm\Events\Frontend\MetaInformation\EventMetaInformationInterface;
use Wrm\Events\Domain\Model\Dto\EventDemandFactory;
use Wrm\Events\Domain\Model\Event;
use Wrm\Events\Domain\Repository\EventRepository;
@ -25,14 +26,21 @@ class EventController extends AbstractController
*/
protected $demandFactory;
/**
* @var EventMetaInformationInterface
*/
protected $metaInformationService;
public function __construct(
EventRepository $eventRepository,
DataProcessingForModels $dataProcessing,
EventDemandFactory $demandFactory
EventDemandFactory $demandFactory,
EventMetaInformationInterface $metaInformationService
) {
$this->eventRepository = $eventRepository;
$this->dataProcessing = $dataProcessing;
$this->demandFactory = $demandFactory;
$this->metaInformationService = $metaInformationService;
}
protected function initializeAction(): void
@ -54,6 +62,7 @@ class EventController extends AbstractController
*/
public function showAction(Event $event): void
{
$this->metaInformationService->setEvent($event);
$this->view->assign('event', $event);
}

View file

@ -120,6 +120,11 @@ class Event extends AbstractEntity
*/
protected $features;
/**
* @var string
*/
protected $keywords = '';
/**
* @var ObjectStorage<Partner>
*/
@ -437,6 +442,11 @@ class Event extends AbstractEntity
return $this->getSortedCategory($this->features);
}
public function getKeywords(): string
{
return $this->keywords;
}
public function setLanguageUid(int $languageUid): void
{
$this->_languageUid = $languageUid;

View file

@ -0,0 +1,31 @@
<?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\Frontend\MetaInformation;
use Wrm\Events\Domain\Model\Date;
interface DateMetaInformationInterface
{
public function setDate(Date $date): void;
}

View file

@ -0,0 +1,84 @@
<?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\Frontend\MetaInformation;
use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
use Wrm\Events\Domain\Model\Date;
/**
* TYPO3 has many different APIs to set meta information like: Page Title, Meta Tags, OpenGraph Tags, etc.
* Those are combined here for Date detail view.
* That way there is a single place to connect the details to TYPO3 APIs.
*/
final class DateMetaInformationService implements DateMetaInformationInterface
{
/**
* @var MetaTagManagerRegistry
*/
private $metaTagManagerRegistry;
public function __construct(
MetaTagManagerRegistry $metaTagManagerRegistry
) {
$this->metaTagManagerRegistry = $metaTagManagerRegistry;
}
public function setDate(Date $date): void
{
$this->setDescription($date);
$this->setKeywords($date);
}
private function setDescription(Date $date): void
{
$description = '';
if ($date->getEvent() !== null) {
$description = $date->getEvent()->getTeaser();
}
if ($description === '') {
return;
}
$this->metaTagManagerRegistry
->getManagerForProperty('description')
->addProperty('description', $description)
;
}
private function setKeywords(Date $date): void
{
$keywords = '';
if ($date->getEvent() !== null) {
$keywords = $date->getEvent()->getKeywords();
}
if ($keywords === '') {
return;
}
$this->metaTagManagerRegistry
->getManagerForProperty('keywords')
->addProperty('keywords', $keywords)
;
}
}

View file

@ -0,0 +1,31 @@
<?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\Frontend\MetaInformation;
use Wrm\Events\Domain\Model\Event;
interface EventMetaInformationInterface
{
public function setEvent(Event $event): void;
}

View file

@ -0,0 +1,78 @@
<?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\Frontend\MetaInformation;
use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
use Wrm\Events\Domain\Model\Event;
/**
* TYPO3 has many different APIs to set meta information like: Page Title, Meta Tags, OpenGraph Tags, etc.
* Those are combined here for Event detail view.
* That way there is a single place to connect the details to TYPO3 APIs.
*/
final class EventMetaInformationService implements EventMetaInformationInterface
{
/**
* @var MetaTagManagerRegistry
*/
private $metaTagManagerRegistry;
public function __construct(
MetaTagManagerRegistry $metaTagManagerRegistry
) {
$this->metaTagManagerRegistry = $metaTagManagerRegistry;
}
public function setEvent(Event $event): void
{
$this->setDescription($event);
$this->setKeywords($event);
}
private function setDescription(Event $event): void
{
$description = $event->getTeaser();
if ($description === '') {
return;
}
$this->metaTagManagerRegistry
->getManagerForProperty('description')
->addProperty('description', $description)
;
}
private function setKeywords(Event $event): void
{
$keywords = $event->getKeywords();
if ($keywords === '') {
return;
}
$this->metaTagManagerRegistry
->getManagerForProperty('keywords')
->addProperty('keywords', $keywords)
;
}
}

View file

@ -293,13 +293,20 @@ class DestinationDataImportService
$this->persistenceManager->persistAll();
// Apply changes via DataHandler (The new way)
$eventUid = $this->tmpCurrentEvent->getUid();
if (is_int($eventUid) === false) {
throw new Exception('Could not persist and fetch uid of event.', 1701244570);
}
$this->logger->info('Apply changes via DataHandler');
if ($event['categories'] ?? false) {
$this->setCategories($event['categories']);
}
if ($event['features']) {
$this->setFeatures($event['features']);
}
$this->dataHandler->updateEvent(
$eventUid,
[
new Assignment('keywords', implode(', ', $event['keywords'] ?? [])),
$this->getCategories($event['categories'] ?? []),
$this->getFeatures($event['features'] ?? []),
]
);
$this->logger->info('Update slugs');
$this->slugger->update('tx_events_domain_model_event');
@ -313,7 +320,7 @@ class DestinationDataImportService
return 0;
}
private function setCategories(array $categories): void
private function getCategories(array $categories): Assignment
{
$categories = $this->categoriesAssignment->getCategories(new CategoryImport(
$this->import->getCategoryParent(),
@ -327,14 +334,13 @@ class DestinationDataImportService
);
$this->eventDispatcher->dispatch($event);
$this->dataHandler->storeAssignments(new Assignment(
$this->tmpCurrentEvent->getUid(),
return Assignment::createFromDomainObjects(
'categories',
$event->getCategories()->toArray()
));
);
}
private function setFeatures(array $features): void
private function getFeatures(array $features): Assignment
{
$features = $this->categoriesAssignment->getCategories(new CategoryImport(
$this->import->getFeaturesParent(),
@ -343,11 +349,10 @@ class DestinationDataImportService
true
));
$this->dataHandler->storeAssignments(new Assignment(
$this->tmpCurrentEvent->getUid(),
return Assignment::createFromDomainObjects(
'features',
$features->toArray()
));
);
}
private function setDates(

View file

@ -42,25 +42,26 @@ final class DataHandler
$this->logger = $logManager->getLogger(__CLASS__);
}
public function storeAssignments(
Assignment $assignment
/**
* @param Assignment[] $assignments
*/
public function updateEvent(
int $eventUid,
array $assignments
): void {
$data = [
'tx_events_domain_model_event' => [
$assignment->getUid() => [
$assignment->getColumnName() => implode(',', $assignment->getUids()),
],
],
];
$data = ['tx_events_domain_model_event' => [$eventUid => []]];
foreach ($assignments as $assignment) {
$data['tx_events_domain_model_event'][$eventUid][$assignment->getColumnName()] = $assignment->getValue();
}
$this->logger->debug('Import assignment.', $data);
$this->logger->debug('Update event data.', $data);
$dataHandler = GeneralUtility::makeInstance(Typo3DataHandler::class);
$dataHandler->start($data, []);
$dataHandler->process_datamap();
if ($dataHandler->errorLog !== []) {
$this->logger->error('Error during import of assignments.', [
'assignment' => $assignment,
$this->logger->error('Error during update of event data.', [
'assignments' => $assignments,
'errors' => $dataHandler->errorLog,
]);
}

View file

@ -28,43 +28,22 @@ use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
final class Assignment
{
/**
* @var int
*/
private $uid;
/**
* @var string
*/
private $columnName;
/**
* @var int[]
* @var string
*/
private $uids;
private $value;
/**
* @param AbstractDomainObject[] $assignments
*/
public function __construct(
int $uid,
string $columnName,
array $assignments
string $value
) {
$this->uid = $uid;
$this->columnName = $columnName;
$this->uids = array_map(static function (AbstractDomainObject $model): int {
$uid = $model->getUid();
if (is_int($uid) === false) {
throw new InvalidArgumentException('Only object with uid supported.', 1698936965);
}
return $uid;
}, $assignments);
}
public function getUid(): int
{
return $this->uid;
$this->value = $value;
}
public function getColumnName(): string
@ -72,11 +51,29 @@ final class Assignment
return $this->columnName;
}
/**
* @return int[]
*/
public function getUids(): array
public function getValue(): string
{
return $this->uids;
return $this->value;
}
/**
* @param AbstractDomainObject[] $objects
*/
public static function createFromDomainObjects(
string $columnName,
array $objects
): self {
$uids = array_map(static function (AbstractDomainObject $model): int {
$uid = $model->getUid();
if (is_int($uid) === false) {
throw new InvalidArgumentException('Only object with uid supported.', 1698936965);
}
return $uid;
}, $objects);
return new self(
$columnName,
implode(',', $uids)
);
}
}

View file

@ -54,6 +54,7 @@ return [
partner,
categories,
features,
keywords,
references_events,
pages,
--div--;' . $l10nPath . ':tx_events_domain_model_event.tabs.media,
@ -400,6 +401,15 @@ return [
'features' => [
'exclude' => true,
],
'keywords' => [
'exclude' => true,
'label' => 'Keywords',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 3,
],
],
'dates' => [
'exclude' => true,

View file

@ -0,0 +1,34 @@
3.9.0
=====
Breaking
--------
Nothing
Features
--------
Backport of 4.0.0 features:
* Add meta tags.
A new class is added which will add meta tags for dates and events.
The class has an interface which allows it to be replaced via DI to alter behaviour.
* Import keywords for events from destination.one.
That way keywords are available for usage in meta tags.
Fixes
-----
Nothing
Tasks
-----
Nothing
Deprecation
-----------
Nothing

View file

@ -0,0 +1,15 @@
.. index:: single: meta tags
.. _metaTags:
Meta Tags
=========
The extension comes with default implementations for meta tags.
The default implementation can be exchanged by leveraging TYPO3 Dependency Injection.
Further resources:
* https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ApiOverview/Seo/MetaTagApi.html
* https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ApiOverview/DependencyInjection/Index.html

View file

@ -199,4 +199,21 @@ class DatesTest extends AbstractFunctionalTestCase
self::assertStringNotContainsString('Event 1', $html);
self::assertStringContainsString('Event 2', $html);
}
#[Test]
public function addsMetaTags(): void
{
$this->importPHPDataSet(__DIR__ . '/DatesTestFixtures/DateMetaTags.php');
$request = new InternalRequest();
$request = $request->withPageId(1);
$request = $request->withQueryParameter('tx_events_dateshow[date]', '1');
$response = $this->executeFrontendRequest($request);
self::assertSame(200, $response->getStatusCode());
$html = (string)$response->getBody();
self::assertStringContainsString('<meta name="description" content="Teaser of Event" />', $html);
self::assertStringContainsString('<meta name="keywords" content="Gewölbe, Goethe, Horst Damm, Kästner, Theater" />', $html);
}
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
return [
'tt_content' => [
0 => [
'uid' => '1',
'pid' => '1',
'CType' => 'list',
'list_type' => 'events_dateshow',
'header' => 'Singleview',
],
],
'tx_events_domain_model_event' => [
0 => [
'uid' => '1',
'pid' => '2',
'title' => 'Title of Event',
'teaser' => 'Teaser of Event',
'keywords' => 'Gewölbe, Goethe, Horst Damm, Kästner, Theater',
'hidden' => '0',
],
],
'tx_events_domain_model_date' => [
0 => [
'uid' => '1',
'pid' => '2',
'event' => '1',
'start' => '1676419200',
'end' => '1676484000',
],
],
];

View file

@ -0,0 +1,62 @@
<?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\Frontend;
use PHPUnit\Framework\Attributes\Test;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
use Wrm\Events\Tests\Functional\AbstractFunctionalTestCase;
class EventsTest extends AbstractFunctionalTestCase
{
protected function setUp(): void
{
$this->testExtensionsToLoad = [
'typo3conf/ext/events/Tests/Functional/Frontend/Fixtures/Extensions/example',
];
parent::setUp();
$this->importPHPDataSet(__DIR__ . '/Fixtures/Database/SiteStructure.php');
$this->setUpFrontendRendering();
}
/**
* @test
*/
public function addsMetaTags(): void
{
$this->importPHPDataSet(__DIR__ . '/EventsTestFixtures/EventMetaTags.php');
$request = new InternalRequest();
$request = $request->withPageId(1);
$request = $request->withQueryParameter('tx_events_eventshow[event]', '1');
$response = $this->executeFrontendRequest($request);
self::assertSame(200, $response->getStatusCode());
$html = (string)$response->getBody();
self::assertStringContainsString('<meta name="description" content="Teaser of Event" />', $html);
self::assertStringContainsString('<meta name="keywords" content="Gewölbe, Goethe, Horst Damm, Kästner, Theater" />', $html);
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
return [
'tt_content' => [
0 => [
'uid' => '1',
'pid' => '1',
'CType' => 'list',
'list_type' => 'events_eventshow',
'header' => 'Singleview',
],
],
'tx_events_domain_model_event' => [
0 => [
'uid' => '1',
'pid' => '2',
'title' => 'Title of Event',
'teaser' => 'Teaser of Event',
'keywords' => 'Gewölbe, Goethe, Horst Damm, Kästner, Theater',
'hidden' => '0',
],
],
];

View file

@ -5,3 +5,9 @@
'DateListTest',
[\Wrm\Events\Controller\DateController::class => 'list']
);
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Events',
'EventShow',
[\Wrm\Events\Controller\EventController::class => 'show']
);

View file

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
return [
'tx_events_domain_model_event' => [
[
'uid' => 1,
'global_id' => 'e_100347853',
'keywords' => 'Gewölbe, Goethe, Horst Damm, Kästner, Theater, low_image_qualit',
],
[
'uid' => 2,
'global_id' => 'e_100347854',
'keywords' => '',
],
],
];

View file

@ -0,0 +1,153 @@
{
"status": "OK",
"count": 2,
"overallcount": 2,
"channels": [],
"facetGroups": [],
"items": [
{
"global_id": "e_100347853",
"id": "100347853",
"title": "Allerlei Weihnachtliches (Heute mit Johannes Geißer)",
"type": "Event",
"categories": [
],
"texts": [
{
"rel": "details",
"type": "text/html",
"value": "Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert.<br>Eintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke)<br>Um Voranmeldung unter 03672-486470 oder&nbsp;<a data-cke-saved-href=\"mailto:schillerhaus@rudolstadt.de\" href=\"mailto:schillerhaus@rudolstadt.de\">schillerhaus@rudolstadt.de</a>&nbsp;wird gebeten. <br><strong>Es gilt die 2G-PLUS-Regel.</strong>&nbsp;<br>"
},
{
"rel": "details",
"type": "text/plain",
"value": "Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert.\nEintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke)\nUm Voranmeldung unter 03672-486470 oder schillerhaus@rudolstadt.de wird gebeten.\nEs gilt die 2G-PLUS-Regel."
},
{
"rel": "teaser",
"type": "text/html"
},
{
"rel": "teaser",
"type": "text/plain"
}
],
"country": "Deutschland",
"areas": [
"Rudolstadt und Umgebung"
],
"city": "Rudolstadt",
"zip": "07407",
"street": "Schillerstraße 25",
"phone": "+ 49 3672 / 486470",
"fax": "+ 49 3672 / 486475",
"web": "http://www.schillerhaus.rudolstadt.de/",
"email": "schillerhaus@rudolstadt.de",
"author": "support@hubermedia.de",
"media_objects": [
],
"keywords": [
"Gewölbe",
"Goethe",
"Horst Damm",
"Kästner",
"Theater",
"low_image_qualit"
],
"timeIntervals": [
{
"weekdays": [],
"start": "2099-12-19T15:00:00+01:00",
"end": "2099-12-19T16:30:00+01:00",
"tz": "Europe/Berlin",
"interval": 1
}
],
"name": "Schillerhaus Rudolstadt",
"features": [
],
"addresses": [
],
"created": "2099-10-31T12:29:00+00:00",
"changed": "2099-12-14T08:29:00+00:00",
"company": "",
"district": "",
"postoffice": "",
"phone2": "",
"seasons": [],
"subitems": [],
"hyperObjects": []
},
{
"global_id": "e_100347854",
"id": "100347854",
"title": "Allerlei Weihnachtliches (Heute mit Johannes Geißer)",
"type": "Event",
"categories": [
],
"texts": [
{
"rel": "details",
"type": "text/html",
"value": "Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert.<br>Eintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke)<br>Um Voranmeldung unter 03672-486470 oder&nbsp;<a data-cke-saved-href=\"mailto:schillerhaus@rudolstadt.de\" href=\"mailto:schillerhaus@rudolstadt.de\">schillerhaus@rudolstadt.de</a>&nbsp;wird gebeten. <br><strong>Es gilt die 2G-PLUS-Regel.</strong>&nbsp;<br>"
},
{
"rel": "details",
"type": "text/plain",
"value": "Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert.\nEintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke)\nUm Voranmeldung unter 03672-486470 oder schillerhaus@rudolstadt.de wird gebeten.\nEs gilt die 2G-PLUS-Regel."
},
{
"rel": "teaser",
"type": "text/html"
},
{
"rel": "teaser",
"type": "text/plain"
}
],
"country": "Deutschland",
"areas": [
"Rudolstadt und Umgebung"
],
"city": "Rudolstadt",
"zip": "07407",
"street": "Schillerstraße 25",
"phone": "+ 49 3672 / 486470",
"fax": "+ 49 3672 / 486475",
"web": "http://www.schillerhaus.rudolstadt.de/",
"email": "schillerhaus@rudolstadt.de",
"author": "support@hubermedia.de",
"media_objects": [
],
"keywords": [
],
"timeIntervals": [
{
"weekdays": [],
"start": "2099-12-19T15:00:00+01:00",
"end": "2099-12-19T16:30:00+01:00",
"tz": "Europe/Berlin",
"interval": 1
}
],
"name": "Schillerhaus Rudolstadt",
"features": [
],
"addresses": [
],
"created": "2099-10-31T12:29:00+00:00",
"changed": "2099-12-14T08:29:00+00:00",
"source": {
"url": "https://example.com",
"value": "Foreign Example 1"
},
"company": "",
"district": "",
"postoffice": "",
"phone2": "",
"seasons": [],
"subitems": [],
"hyperObjects": []
}
]
}

View file

@ -94,4 +94,18 @@ class ImportsExampleAsExpectedTest extends AbstractTest
$this->assertPHPDataSet(__DIR__ . '/Assertions/ImportsSource.php');
$this->assertEmptyLog();
}
#[Test]
public function importsKeywords(): void
{
$this->importPHPDataSet(__DIR__ . '/Fixtures/Database/DefaultImportConfiguration.php');
$this->setUpResponses([
new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ResponseWithKeywords.json') ?: ''),
]);
$this->executeCommand();
$this->assertPHPDataSet(__DIR__ . '/Assertions/ImportsKeywords.php');
$this->assertEmptyLog();
}
}

View file

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

View file

@ -15,6 +15,7 @@ CREATE TABLE tx_events_domain_model_event (
images int(11) unsigned NOT NULL default '0',
categories int(11) DEFAULT '0' NOT NULL,
features int(11) DEFAULT '0' NOT NULL,
keywords text,
pages text,
dates int(11) unsigned DEFAULT '0' NOT NULL,
organizer int(11) unsigned DEFAULT '0',

View file

@ -90,6 +90,11 @@ parameters:
count: 1
path: Classes/Service/Cleanup/Files.php
-
message: "#^Parameter \\#2 \\$objects of static method Wrm\\\\Events\\\\Service\\\\DestinationDataImportService\\\\DataHandler\\\\Assignment\\:\\:createFromDomainObjects\\(\\) expects array\\<TYPO3\\\\CMS\\\\Extbase\\\\DomainObject\\\\AbstractDomainObject\\>, array\\<int, TEntity\\> given\\.$#"
count: 2
path: Classes/Service/DestinationDataImportService.php
-
message: "#^Parameter \\#1 \\$uid of class Wrm\\\\Events\\\\Service\\\\DestinationDataImportService\\\\DataHandler\\\\Assignment constructor expects int, int\\|null given\\.$#"
count: 2