From 46922636d66a92b9271f8bf6b2ea8044f912c65b Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 29 Sep 2022 08:41:44 +0200 Subject: [PATCH] Add form integration (#6) --- Classes/Domain/Model/FormElementAwareItem.php | 40 +++++++++ Classes/Domain/Model/Watchlist.php | 11 +++ .../FormElements/WatchlistFormElement.php | 47 ++++++++++ Classes/Form/Hook/BeforeRenderingHook.php | 63 +++++++++++++ Configuration/Form/Setup.yaml | 8 ++ Configuration/Services.yaml | 3 + README.rst | 50 ++++++++++- Tests/Acceptance/FormIntegrationCest.php | 56 ++++++++++++ Tests/Acceptance/Support/Environment.php | 2 + Tests/Fixtures/FormDatabase.csv | 17 ++++ .../watchlist_example/Classes/Page.php | 13 ++- .../Configuration/Forms/Example.form.yaml | 31 +++++++ .../Configuration/Forms/Setup.yaml | 14 +++ .../Configuration/TypoScript/Form.typoscript | 10 +++ .../Partials/Form/Frontend/Watchlist.html | 26 ++++++ .../Fixtures/watchlist_example/composer.json | 2 + Tests/Functional/BasicsTest.php | 3 - Tests/Functional/FormIntegrationTest.php | 89 +++++++++++++++++++ composer.json | 1 + ext_localconf.php | 7 +- 20 files changed, 483 insertions(+), 10 deletions(-) create mode 100644 Classes/Domain/Model/FormElementAwareItem.php create mode 100644 Classes/Form/FormElements/WatchlistFormElement.php create mode 100644 Classes/Form/Hook/BeforeRenderingHook.php create mode 100644 Configuration/Form/Setup.yaml create mode 100644 Tests/Acceptance/FormIntegrationCest.php create mode 100644 Tests/Fixtures/FormDatabase.csv create mode 100644 Tests/Fixtures/watchlist_example/Configuration/Forms/Example.form.yaml create mode 100644 Tests/Fixtures/watchlist_example/Configuration/Forms/Setup.yaml create mode 100644 Tests/Fixtures/watchlist_example/Configuration/TypoScript/Form.typoscript create mode 100644 Tests/Fixtures/watchlist_example/Resources/Private/Partials/Form/Frontend/Watchlist.html create mode 100644 Tests/Functional/FormIntegrationTest.php diff --git a/Classes/Domain/Model/FormElementAwareItem.php b/Classes/Domain/Model/FormElementAwareItem.php new file mode 100644 index 0000000..a75768a --- /dev/null +++ b/Classes/Domain/Model/FormElementAwareItem.php @@ -0,0 +1,40 @@ + + * + * 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\Watchlist\Domain\Model; + +/** + * Needs to be implemented by all Items which should be usable for form integration. + */ +interface FormElementAwareItem +{ + /** + * Provides the actual value to use when submitting the element. + */ + public function getFormElementValue(): string; + + /** + * Provides the actual title to use as label within the element. + */ + public function getFormElementTitle(): string; +} diff --git a/Classes/Domain/Model/Watchlist.php b/Classes/Domain/Model/Watchlist.php index 93618d5..87f27b8 100644 --- a/Classes/Domain/Model/Watchlist.php +++ b/Classes/Domain/Model/Watchlist.php @@ -43,6 +43,17 @@ class Watchlist return array_values($this->items); } + /** + * @return array + */ + public function getFormElementAwareItems(): array + { + return array_filter( + array_values($this->items), + fn (Item $item) => $item instanceof FormElementAwareItem + ); + } + public function addItem(Item $item): void { $this->items[$item->getUniqueIdentifier()] = $item; diff --git a/Classes/Form/FormElements/WatchlistFormElement.php b/Classes/Form/FormElements/WatchlistFormElement.php new file mode 100644 index 0000000..4e2eaad --- /dev/null +++ b/Classes/Form/FormElements/WatchlistFormElement.php @@ -0,0 +1,47 @@ + + * + * 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\Watchlist\Form\FormElements; + +use TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement; +use WerkraumMedia\Watchlist\Domain\Model\Watchlist; +use WerkraumMedia\Watchlist\Session\SessionServiceInterface; + +final class WatchlistFormElement extends AbstractFormElement +{ + private SessionServiceInterface $sessionService; + + public function injectSessionService(SessionServiceInterface $sessionService): void + { + $this->sessionService = $sessionService; + } + + public function getWatchlist(): ?Watchlist + { + // Prevent watchlist items from being cached for users. + // Only trigger once they are actually used = this method is used. + $GLOBALS['TSFE']->no_cache = 1; + + return $this->sessionService->getWatchlist('default'); + } +} diff --git a/Classes/Form/Hook/BeforeRenderingHook.php b/Classes/Form/Hook/BeforeRenderingHook.php new file mode 100644 index 0000000..1707ef5 --- /dev/null +++ b/Classes/Form/Hook/BeforeRenderingHook.php @@ -0,0 +1,63 @@ + + * + * 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\Watchlist\Form\Hook; + +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +use WerkraumMedia\Watchlist\Form\FormElements\WatchlistFormElement; +use WerkraumMedia\Watchlist\Session\SessionServiceInterface; + +/** + * Renderables are instantiated with makeInstance and constructor arguments. + * We therefore have no chance for DI. + * + * That's why we use the hook as this seems like the best workaround. + * He will use the inject* method, so we can remove the hook once DI should work. + */ +final class BeforeRenderingHook +{ + private SessionServiceInterface $sessionService; + + public function __construct( + SessionServiceInterface $sessionService + ) { + $this->sessionService = $sessionService; + } + + public function beforeRendering( + FormRuntime $formRuntime, + RootRenderableInterface $renderable + ): void { + if (!$renderable instanceof WatchlistFormElement) { + return; + } + + $renderable->injectSessionService($this->sessionService); + } + + public static function register(): void + { + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'][self::class] = self::class; + } +} diff --git a/Configuration/Form/Setup.yaml b/Configuration/Form/Setup.yaml new file mode 100644 index 0000000..bb719c8 --- /dev/null +++ b/Configuration/Form/Setup.yaml @@ -0,0 +1,8 @@ +TYPO3: + CMS: + Form: + prototypes: + standard: + formElementsDefinition: + Watchlist: + implementationClassName: WerkraumMedia\Watchlist\Form\FormElements\WatchlistFormElement diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 189cfd8..40ba7c8 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -12,3 +12,6 @@ services: WerkraumMedia\Watchlist\Session\CookieSessionService: # Has state and needs to be shared in order to provide this state to middleware shared: true + + WerkraumMedia\Watchlist\Form\Hook\BeforeRenderingHook: + public: true diff --git a/README.rst b/README.rst index d144e6c..70a6e24 100644 --- a/README.rst +++ b/README.rst @@ -25,15 +25,27 @@ Projects are way too different and should provide their own items, see "Custom I The extension also provides a JavaScript which will parse data-Attributes of DOM and attach listener to add elements to the watchlist. +Allows to render the items of watchlist within an EXT:form form. + +Concept +======= + +The extension only provides the functionality, no concrete project specific implementation. +It is up to the developer and integrator of the project to provide necessary pieces, see "Custom Items". +The extension always only uses identifier of each item and does not know anything else about items. + Custom Items ============ A developer needs to implement an ``ItemHandler`` and an class representing the ``Item``. +``FormElementAwareItem`` can optionally be implemented as well, in order to make use of items within an EXT:form form. + +The used identifier has to be unique throughout the system. +The first part is the item type which is returned by the ItemHandler to be handled, +followed by a minus and the rest of the identifier, leading to: ``-rest-of-identifier``. -The used identifier has to be unique throughout the system. The first part is the -item type which is returned by the ItemHandler to be handled, followed by a minus and -the rest of the identifier, leading to: ``-rest-of-identifier``. Commas should not be used within the identifier. +The identifiers will be stored in as csv within a cookie. ``ItemHandler`` --------------- @@ -52,6 +64,14 @@ The Handler needs to be registered via Symfony Tags, e.g. via ``Services.yaml``: The class needs to implement the ``WerkraumMedia\Watchlist\Domain\Model\Item``. +``FormElementAwareItem`` +------------------------ + +The class can implement the ``WerkraumMedia\Watchlist\Domain\Model\FormElementAwareItem``. + +The methods are used by the default EXT:form integration. +It is up to the developer + integrator to not use this interface and alter the rendering. + Example ------- @@ -60,12 +80,36 @@ The extension delivers an example implementation for testing purposes, check out - ``Tests/Fixtures/WatchlistExample/Classes/Domain/Items/Page/ItemHandler.php`` - ``Tests/Fixtures/WatchlistExample/Classes/Domain/Items/Page/Page.php`` + This implements both ``Item`` as well as ``FormElementAwareItem``. - ``Tests/Fixtures/WatchlistExample/Configuration/Services.yaml`` The example demonstrates how to fetch information from database, including file references. +EXT:form integration +==================== + +The provided Configuration needs to be loaded via TypoScript. +Use a free identifier: + +.. code:: plain + + plugin.tx_form.settings.yamlConfigurations { + 80 = EXT:watchlist/Configuration/Form/Setup.yaml + } + +This will register a new form element type ``Watchlist`` that can be used like this: + +.. code:: yaml + + - + type: Watchlist + identifier: watchlist-1 + label: 'Watchlist' + +A default template is provided which will render all items with checkboxes. + JavaScript ========== diff --git a/Tests/Acceptance/FormIntegrationCest.php b/Tests/Acceptance/FormIntegrationCest.php new file mode 100644 index 0000000..d69035c --- /dev/null +++ b/Tests/Acceptance/FormIntegrationCest.php @@ -0,0 +1,56 @@ + + * + * 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\Watchlist\Tests\Acceptance; + +use WerkraumMedia\Watchlist\Tests\Acceptance\Support\AcceptanceTester; + +class FormIntegrationCest +{ + public function canSelectOneOfTwoSubmittedItemsFromWatchlist(AcceptanceTester $I): void + { + $I->amOnPage('/'); + $I->click('button[data-watchlist-item="page-1"]'); + $I->click('button[data-watchlist-item="page-2"]'); + + $I->amOnPage('/page-2'); + $I->checkOption('#test-1-watchlist-1-1'); + $I->click('Next step'); + + $I->see('Page: Page 2 Title'); + $I->dontSee('Page: Page Title'); + $I->click('Submit'); + } + + public function watchlistItemsInFormAreNotCached(AcceptanceTester $I): void + { + $this->canSelectOneOfTwoSubmittedItemsFromWatchlist($I); + + $I->amOnPage('/'); + $I->click('button[data-watchlist-item="page-1"]'); + + $I->amOnPage('/page-2'); + $I->see('Form: Page 2 Title'); + $I->dontSee('Form: Page Title'); + } +} diff --git a/Tests/Acceptance/Support/Environment.php b/Tests/Acceptance/Support/Environment.php index ca554ca..5204c45 100644 --- a/Tests/Acceptance/Support/Environment.php +++ b/Tests/Acceptance/Support/Environment.php @@ -44,6 +44,7 @@ class Environment extends BackendEnvironment 'frontend', 'fluid', 'fluid_styled_content', + 'form', ], 'testExtensionsToLoad' => [ 'typo3conf/ext/watchlist', @@ -51,6 +52,7 @@ class Environment extends BackendEnvironment ], 'csvDatabaseFixtures' => [ __DIR__ . '/../../Fixtures/BasicDatabase.csv', + __DIR__ . '/../../Fixtures/FormDatabase.csv', ], 'additionalFoldersToCreate' => [ 'config', diff --git a/Tests/Fixtures/FormDatabase.csv b/Tests/Fixtures/FormDatabase.csv new file mode 100644 index 0000000..b176ead --- /dev/null +++ b/Tests/Fixtures/FormDatabase.csv @@ -0,0 +1,17 @@ +"sys_template",,,,, +,"pid","config",,, +,2," +""",,, +"tt_content",,,,, +,"pid","header","CType","pi_flexform", +,2,"Form","form_formframework"," + + + + + EXT:watchlist_example/Configuration/Forms/Example.form.yaml + + + + +", diff --git a/Tests/Fixtures/watchlist_example/Classes/Page.php b/Tests/Fixtures/watchlist_example/Classes/Page.php index 7a90385..986da47 100644 --- a/Tests/Fixtures/watchlist_example/Classes/Page.php +++ b/Tests/Fixtures/watchlist_example/Classes/Page.php @@ -24,9 +24,10 @@ declare(strict_types=1); namespace WerkraumMedia\WatchlistExample; use TYPO3\CMS\Core\Resource\FileInterface; +use WerkraumMedia\Watchlist\Domain\Model\FormElementAwareItem; use WerkraumMedia\Watchlist\Domain\Model\Item; -class Page implements Item +class Page implements Item, FormElementAwareItem { private int $pageUid; @@ -58,4 +59,14 @@ class Page implements Item { return $this->image; } + + public function getFormElementValue(): string + { + return 'Page: ' . $this->title; + } + + public function getFormElementTitle(): string + { + return 'Form: ' . $this->title; + } } diff --git a/Tests/Fixtures/watchlist_example/Configuration/Forms/Example.form.yaml b/Tests/Fixtures/watchlist_example/Configuration/Forms/Example.form.yaml new file mode 100644 index 0000000..5a2ba52 --- /dev/null +++ b/Tests/Fixtures/watchlist_example/Configuration/Forms/Example.form.yaml @@ -0,0 +1,31 @@ +renderingOptions: + submitButtonLabel: Submit +type: Form +identifier: test +label: Test +prototypeName: standard +renderables: + - + renderingOptions: + previousButtonLabel: 'Previous step' + nextButtonLabel: 'Next step' + type: Page + identifier: page-1 + label: First Step + renderables: + - + type: Watchlist + identifier: watchlist-1 + label: 'Watchlist' + - + defaultValue: '' + type: Text + identifier: text-1 + label: 'Example text field' + - + renderingOptions: + previousButtonLabel: 'Previous step' + nextButtonLabel: 'Next step' + type: SummaryPage + identifier: summarypage-1 + label: 'Summary step' diff --git a/Tests/Fixtures/watchlist_example/Configuration/Forms/Setup.yaml b/Tests/Fixtures/watchlist_example/Configuration/Forms/Setup.yaml new file mode 100644 index 0000000..075b5c0 --- /dev/null +++ b/Tests/Fixtures/watchlist_example/Configuration/Forms/Setup.yaml @@ -0,0 +1,14 @@ +TYPO3: + CMS: + Form: + persistenceManager: + allowedExtensionPaths: + 10: EXT:watchlist_example/Configuration/Forms/ + + prototypes: + standard: + formElementsDefinition: + Form: + renderingOptions: + partialRootPaths: + 99: 'EXT:watchlist_example/Resources/Private/Partials/Form/Frontend/' diff --git a/Tests/Fixtures/watchlist_example/Configuration/TypoScript/Form.typoscript b/Tests/Fixtures/watchlist_example/Configuration/TypoScript/Form.typoscript new file mode 100644 index 0000000..f0556ef --- /dev/null +++ b/Tests/Fixtures/watchlist_example/Configuration/TypoScript/Form.typoscript @@ -0,0 +1,10 @@ + + +plugin.tx_form.settings.yamlConfigurations { + 80 = EXT:watchlist/Configuration/Form/Setup.yaml + 90 = EXT:watchlist_example/Configuration/Forms/Setup.yaml +} + +page > +page = PAGE +page.10 =< styles.content.get diff --git a/Tests/Fixtures/watchlist_example/Resources/Private/Partials/Form/Frontend/Watchlist.html b/Tests/Fixtures/watchlist_example/Resources/Private/Partials/Form/Frontend/Watchlist.html new file mode 100644 index 0000000..50ada61 --- /dev/null +++ b/Tests/Fixtures/watchlist_example/Resources/Private/Partials/Form/Frontend/Watchlist.html @@ -0,0 +1,26 @@ + + + + + + +
+ +
+
+
+
+
+ + diff --git a/Tests/Fixtures/watchlist_example/composer.json b/Tests/Fixtures/watchlist_example/composer.json index 18f5357..81df862 100644 --- a/Tests/Fixtures/watchlist_example/composer.json +++ b/Tests/Fixtures/watchlist_example/composer.json @@ -4,6 +4,8 @@ "type": "typo3-cms-extension", "license": "GPL-2.0-or-later", "require": { + "typo3/cms-core": "*", + "typo3/cms-form": "*", "werkraummedia/watchlist": "*" }, "extra": { diff --git a/Tests/Functional/BasicsTest.php b/Tests/Functional/BasicsTest.php index fc9ba14..75ec72f 100644 --- a/Tests/Functional/BasicsTest.php +++ b/Tests/Functional/BasicsTest.php @@ -28,9 +28,6 @@ use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest; use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalResponse; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; -/** - * @covers \WerkraumMedia\Watchlist\Frontend\Basics - */ class BasicsTest extends FunctionalTestCase { protected $coreExtensionsToLoad = [ diff --git a/Tests/Functional/FormIntegrationTest.php b/Tests/Functional/FormIntegrationTest.php new file mode 100644 index 0000000..edc6755 --- /dev/null +++ b/Tests/Functional/FormIntegrationTest.php @@ -0,0 +1,89 @@ + + * + * 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\Watchlist\Tests\Functional; + +use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +class FormIntegrationTest extends FunctionalTestCase +{ + protected $coreExtensionsToLoad = [ + 'fluid_styled_content', + 'form', + 'tstemplate', + ]; + + protected $testExtensionsToLoad = [ + 'typo3conf/ext/watchlist', + 'typo3conf/ext/watchlist/Tests/Fixtures/watchlist_example', + ]; + + protected $pathsToLinkInTestInstance = [ + 'typo3conf/ext/watchlist/Tests/Fixtures/Sites' => 'typo3conf/sites', + 'typo3conf/ext/watchlist/Tests/Fixtures/Fileadmin/Files' => 'fileadmin/Files', + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->importCSVDataSet(__DIR__ . '/../Fixtures/BasicDatabase.csv'); + $this->importCSVDataSet(__DIR__ . '/../Fixtures/FormDatabase.csv'); + } + + /** + * @test + */ + public function rendersWatchlistItemsIntoForm(): void + { + $request = new InternalRequest(); + $request = $request->withPageId(2); + $request = $request->withHeader('Cookie', 'watchlist=page-1,page-2'); + $result = $this->executeFrontendRequest($request); + + self::assertSame(200, $result->getStatusCode()); + $html = $result->getBody()->__toString(); + self::assertSame(2, substr_count($html, 'form-group')); + self::assertStringContainsString('Form: Page Title', $html); + self::assertStringContainsString('value="Page: Page Title"', $html); + self::assertStringContainsString('Form: Page 2 Title', $html); + self::assertStringContainsString('value="Page: Page 2 Title"', $html); + } + + /** + * @test + */ + public function doesntRenderFormElementForZeroItems(): void + { + $request = new InternalRequest(); + $request = $request->withPageId(2); + $result = $this->executeFrontendRequest($request); + + self::assertSame(200, $result->getStatusCode()); + $html = $result->getBody()->__toString(); + self::assertSame(1, substr_count($html, 'form-group')); + self::assertStringNotContainsString('checkbox', $html); + self::assertStringNotContainsString('Watchlist', $html); + } +} diff --git a/composer.json b/composer.json index e9fad40..e5e0ddd 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "phpunit/phpunit": "^9.5", "saschaegerer/phpstan-typo3": "^1.1", "typo3/cms-fluid-styled-content": "^11.5", + "typo3/cms-form": "^11.5", "typo3/cms-tstemplate": "^11.5", "typo3/testing-framework": "^6.6" }, diff --git a/ext_localconf.php b/ext_localconf.php index 3d56bca..3e63a13 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -5,6 +5,7 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility; use WerkraumMedia\Watchlist\Controller\WatchlistController; use WerkraumMedia\Watchlist\Extbase\TypeConverter\ItemTypeConverter; use WerkraumMedia\Watchlist\Extbase\TypeConverter\WatchlistTypeConverter; +use WerkraumMedia\Watchlist\Form\Hook\BeforeRenderingHook; defined('TYPO3') || die('Access denied.'); @@ -20,10 +21,10 @@ defined('TYPO3') || die('Access denied.'); ], ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); - - ExtensionUtility::registerTypeConverter(WatchlistTypeConverter::class); - ExtensionUtility::registerTypeConverter(ItemTypeConverter::class); ExtensionManagementUtility::addPageTSConfig( "@import 'EXT:watchlist/Configuration/TSconfig/Page/Default.tsconfig'" ); + ExtensionUtility::registerTypeConverter(WatchlistTypeConverter::class); + ExtensionUtility::registerTypeConverter(ItemTypeConverter::class); + BeforeRenderingHook::register(); })();