From 66767fa5d09f00d68c38bc4a0746a8bf66bbfabd Mon Sep 17 00:00:00 2001 From: Oliver Kroener Date: Mon, 27 Jan 2025 03:15:43 +0000 Subject: [PATCH 1/7] [FEATURE] In MenuProcessor allow to specify data fields to include as list field1,field2,... --- .../DataProcessing/DataProcessingTrait.php | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Classes/DataProcessing/DataProcessingTrait.php b/Classes/DataProcessing/DataProcessingTrait.php index 844cbd4a..cc8f77f1 100644 --- a/Classes/DataProcessing/DataProcessingTrait.php +++ b/Classes/DataProcessing/DataProcessingTrait.php @@ -20,18 +20,31 @@ trait DataProcessingTrait */ protected function removeDataIfnotAppendInConfiguration(array $processorConfiguration, array $processedData): array { - if (!isset($processorConfiguration['appendData']) || - (int)$processorConfiguration['appendData'] === 0) { - unset($processedData['data']); - if (isset($processorConfiguration['as'], $processedData[$processorConfiguration['as']]) - && is_array($processedData[$processorConfiguration['as']])) { + if ( + !isset($processorConfiguration['appendData']) || + (int)$processorConfiguration['appendData'] !== 1 + ) { + // Items to keep + $removeAll = !isset($processorConfiguration['appendData']) || $processorConfiguration['appendData'] == 0; + $keepItems = $removeAll ? [] : array_flip(array_map('trim', explode(',', $processorConfiguration['appendData']))); + if ($removeAll) + unset($processedData['data']); + else + $processedData['data'] = array_intersect_key($processedData['data'], $keepItems); + if ( + isset($processorConfiguration['as'], $processedData[$processorConfiguration['as']]) + && is_array($processedData[$processorConfiguration['as']]) + ) { foreach ($processedData[$processorConfiguration['as']] as &$item) { if (is_array($item) && isset($item['data'])) { - unset($item['data']); + if ($removeAll) + unset($item['data']); + else + $item['data'] = array_intersect_key($item['data'], $keepItems); } if ($this->isMenuProcessor() && isset($item['children']) && is_array($item['children'])) { - $this->removeDataInChildrenNodes($item['children']); + $this->removeDataInChildrenNodes($item['children'], $removeAll, $keepItems); } } } @@ -49,12 +62,17 @@ protected function isMenuProcessor(): bool * Removes recursively "data" in children nodes * * @param array $children + * @param bool $removeAll + * @param array $keepItems * @param string $nodeName */ - private function removeDataInChildrenNodes(array &$children, string $nodeName = 'children'): void + private function removeDataInChildrenNodes(array &$children, bool $removeAll, array $keepItems, string $nodeName = 'children'): void { foreach ($children as &$childrenItem) { - unset($childrenItem['data']); + if ($removeAll) + unset($childrenItem['data']); + else + $childrenItem['data'] = array_intersect_key($childrenItem['data'], $keepItems); if (isset($childrenItem[$nodeName]) && is_array($childrenItem[$nodeName])) { $this->removeDataInChildrenNodes($childrenItem[$nodeName], $nodeName); } From 9f64a5b596c14dad240ac4c8263b8ff39402edae Mon Sep 17 00:00:00 2001 From: Oliver Kroener Date: Sat, 8 Feb 2025 17:36:53 +0000 Subject: [PATCH 2/7] [TASK] Fixed code --- Classes/DataProcessing/DataProcessingTrait.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Classes/DataProcessing/DataProcessingTrait.php b/Classes/DataProcessing/DataProcessingTrait.php index cc8f77f1..bf9b0242 100644 --- a/Classes/DataProcessing/DataProcessingTrait.php +++ b/Classes/DataProcessing/DataProcessingTrait.php @@ -27,20 +27,22 @@ protected function removeDataIfnotAppendInConfiguration(array $processorConfigur // Items to keep $removeAll = !isset($processorConfiguration['appendData']) || $processorConfiguration['appendData'] == 0; $keepItems = $removeAll ? [] : array_flip(array_map('trim', explode(',', $processorConfiguration['appendData']))); - if ($removeAll) + if ($removeAll) { unset($processedData['data']); - else + } else { $processedData['data'] = array_intersect_key($processedData['data'], $keepItems); + } if ( isset($processorConfiguration['as'], $processedData[$processorConfiguration['as']]) && is_array($processedData[$processorConfiguration['as']]) ) { foreach ($processedData[$processorConfiguration['as']] as &$item) { if (is_array($item) && isset($item['data'])) { - if ($removeAll) + if ($removeAll) { unset($item['data']); - else + } else { $item['data'] = array_intersect_key($item['data'], $keepItems); + } } if ($this->isMenuProcessor() && isset($item['children']) && is_array($item['children'])) { @@ -69,10 +71,11 @@ protected function isMenuProcessor(): bool private function removeDataInChildrenNodes(array &$children, bool $removeAll, array $keepItems, string $nodeName = 'children'): void { foreach ($children as &$childrenItem) { - if ($removeAll) + if ($removeAll) { unset($childrenItem['data']); - else + } else { $childrenItem['data'] = array_intersect_key($childrenItem['data'], $keepItems); + } if (isset($childrenItem[$nodeName]) && is_array($childrenItem[$nodeName])) { $this->removeDataInChildrenNodes($childrenItem[$nodeName], $nodeName); } From 00b2b55fb68c9e03735f30dabb856981041a1079 Mon Sep 17 00:00:00 2001 From: Philipp Kitzberger Date: Thu, 28 Nov 2024 09:32:21 +0100 Subject: [PATCH 3/7] [BUGFIX] Don't render double slashes in file URLs (#796) Without a frontendApiProxy in the site config, URLs to local files contain two slashes like www.mysite.org//fileadmin/my-image.jpg --- Classes/XClass/ResourceLocalDriver.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Classes/XClass/ResourceLocalDriver.php b/Classes/XClass/ResourceLocalDriver.php index 18b4ace1..5caf183b 100644 --- a/Classes/XClass/ResourceLocalDriver.php +++ b/Classes/XClass/ResourceLocalDriver.php @@ -53,10 +53,12 @@ protected function determineBaseUrl(): void }; if ($basePath !== '') { - $frontendUri = (new Uri($urlUtility->getFrontendUrl())); + $frontendUri = new Uri($urlUtility->getFrontendUrl()); + $proxyUri = new Uri($urlUtility->getProxyUrl()); + $baseUri = new Uri($basePath); - $path = new Uri(trim($basePath, '/')); - $this->configuration['baseUri'] = (string)$frontendUri->withPath('/' . trim((new Uri($urlUtility->getProxyUrl()))->getPath(), '/') . '/' . trim($path->getPath(), '/')); + $path = trim($proxyUri->getPath(), '/') . '/' . trim($baseUri->getPath(), '/'); + $this->configuration['baseUri'] = (string)$frontendUri->withPath('/' . trim($path, '/')); } else { $this->configuration['baseUri'] = $urlUtility->getStorageProxyUrl(); } From 086d981a3bea40b89ad40b1efc2942f0ea033c0f Mon Sep 17 00:00:00 2001 From: twoldanski <66474451+twoldanski@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:45:03 +0100 Subject: [PATCH 4/7] [FEATURE] Add links for backend editor on the frontend (#800) This patch prepares backend for nuxt-typo3 implementation. --- Classes/Frontend/BackendEditorUrl.php | 51 +++++++++++++++++++ Configuration/Services.php | 2 + .../Configuration/BackendEditor.typoscript | 14 +++++ .../LoggedUser/BackendEditor.typoscript | 3 ++ .../TypoScript/Mixed/setup.typoscript | 6 +++ Configuration/TypoScript/setup.typoscript | 2 +- 6 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Classes/Frontend/BackendEditorUrl.php create mode 100644 Configuration/TypoScript/Configuration/BackendEditor.typoscript create mode 100644 Configuration/TypoScript/LoggedUser/BackendEditor.typoscript diff --git a/Classes/Frontend/BackendEditorUrl.php b/Classes/Frontend/BackendEditorUrl.php new file mode 100644 index 00000000..bc1d3b2a --- /dev/null +++ b/Classes/Frontend/BackendEditorUrl.php @@ -0,0 +1,51 @@ +generateUrl('pages'); + } + + public function record(): string + { + return $this->generateUrl(); + } + + private function generateUrl(string $table = 'tt_content'): string + { + $beUser = $GLOBALS['BE_USER'] ?? null; + + if ($beUser === null) { + return ''; + } + + $params = [ + 'edit' => [ + $table => [ + '__id__' => 'edit', + ], + ], + ]; + + return (string)$this->uriBuilder->buildUriFromRoute('record_edit', $params, UriBuilder::ABSOLUTE_URL); + } +} diff --git a/Configuration/Services.php b/Configuration/Services.php index c847d1e7..8fe6cf4e 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -26,6 +26,7 @@ use FriendsOfTYPO3\Headless\Event\Listener\HeadlessHreflangGeneratorListener; use FriendsOfTYPO3\Headless\Event\Listener\LoginConfirmedEventListener; use FriendsOfTYPO3\Headless\Form\Service\FormTranslationService; +use FriendsOfTYPO3\Headless\Frontend\BackendEditorUrl; use FriendsOfTYPO3\Headless\Utility\FileUtility; use FriendsOfTYPO3\Headless\Utility\HeadlessFrontendUrlInterface; use FriendsOfTYPO3\Headless\Utility\UrlUtility; @@ -74,6 +75,7 @@ $toLoad->set(IntegerContentObject::class)->tag('frontend.contentobject', ['identifier' => 'INT']); $toLoad->set(FloatContentObject::class)->tag('frontend.contentobject', ['identifier' => 'FLOAT']); + $services->set(BackendEditorUrl::class)->public(); $services->set(FileUtility::class)->public(); $services->set(HeadlessFrontendUrlInterface::class, UrlUtility::class)->autowire(false); $services->set(AfterLinkIsGeneratedListener::class)->tag( diff --git a/Configuration/TypoScript/Configuration/BackendEditor.typoscript b/Configuration/TypoScript/Configuration/BackendEditor.typoscript new file mode 100644 index 00000000..fed46433 --- /dev/null +++ b/Configuration/TypoScript/Configuration/BackendEditor.typoscript @@ -0,0 +1,14 @@ +lib.backendEditor = JSON +lib.backendEditor { + fields { + record = USER_INT + record { + userFunc = FriendsOfTYPO3\Headless\Frontend\BackendEditorUrl->record + } + + page = USER_INT + page { + userFunc = FriendsOfTYPO3\Headless\Frontend\BackendEditorUrl->page + } + } +} diff --git a/Configuration/TypoScript/LoggedUser/BackendEditor.typoscript b/Configuration/TypoScript/LoggedUser/BackendEditor.typoscript new file mode 100644 index 00000000..56b59f94 --- /dev/null +++ b/Configuration/TypoScript/LoggedUser/BackendEditor.typoscript @@ -0,0 +1,3 @@ +[backend.user.isLoggedIn] + initialData.10.fields.backendEditor < lib.backendEditor +[END] diff --git a/Configuration/TypoScript/Mixed/setup.typoscript b/Configuration/TypoScript/Mixed/setup.typoscript index a54d2033..14740afb 100644 --- a/Configuration/TypoScript/Mixed/setup.typoscript +++ b/Configuration/TypoScript/Mixed/setup.typoscript @@ -7,6 +7,8 @@ plugin.tx_headless { [request && traverse(request.getHeaders(), 'accept')[0] == 'application/json'] ## Include page @import "EXT:headless/Configuration/TypoScript/Page/*.typoscript" + ## Include helpers + @import "EXT:headless/Configuration/TypoScript/Helpers/*.typoscript" ## Include content elements @import "EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript" ## Include configuration @@ -19,3 +21,7 @@ plugin.tx_headless { logged.value = 1 } [END] + +[request && traverse(request.getHeaders(), 'accept')[0] == 'application/json' && backend.user.isLoggedIn] + initialData.10.fields.backendEditor < lib.backendEditor +[END] diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index 2a1a831f..8a816471 100644 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -12,4 +12,4 @@ plugin.tx_headless { @import "EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript" ## Include configuration @import "EXT:headless/Configuration/TypoScript/Configuration/*.typoscript" -@import "EXT:headless/Configuration/TypoScript/LoggedUser/FeLogin.typoscript" +@import "EXT:headless/Configuration/TypoScript/LoggedUser/*.typoscript" From 46daed045e80cb22c42c8955a0f643151ab9633e Mon Sep 17 00:00:00 2001 From: twoldanski <66474451+twoldanski@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:57:04 +0100 Subject: [PATCH 5/7] [FEATURE] Add support for `config.htmlTag` & `page.bodyTagAdd` (#802) - By default generate language attributes for html tag based on current language page, so nuxt does not have to do it manually - support custom attributes like `config.htmlTag.attributes.class = no-js` with option to override default attributes - Add support for setting body tag attributes via `page.bodyTagAdd` Typoscript directive, so frontend app can automatically apply it via nuxt feature --- Classes/Seo/MetaHandler.php | 36 +++++++++++++++++++ ...acheableContentIsGeneratedListenerTest.php | 34 ++++++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Classes/Seo/MetaHandler.php b/Classes/Seo/MetaHandler.php index 1f1aa2b3..7a32e9ca 100644 --- a/Classes/Seo/MetaHandler.php +++ b/Classes/Seo/MetaHandler.php @@ -15,12 +15,15 @@ use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\TypoScript\TypoScriptService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Event\ModifyHrefLangTagsEvent; +use function htmlspecialchars; + class MetaHandler { public function __construct( @@ -69,6 +72,26 @@ public function process(ServerRequestInterface $request, TypoScriptFrontendContr $content['seo']['link'] = $seoLinks; } + /** + * @var SiteLanguage $language + */ + $language = $request->getAttribute('language'); + + $rawHtmlTagAttrs = $controller->config['config']['htmlTag.']['attributes.'] ?? []; + $htmlTagAttrs = $this->normalizeAttr($rawHtmlTagAttrs); + + $rawBodyTagAttrs = GeneralUtility::get_tag_attributes(trim($request->getAttribute('frontend.typoscript')->getSetupArray()['page.']['bodyTagAdd'] ?? '')); + $bodyTagAttrs = $this->normalizeAttr($rawBodyTagAttrs); + + $content['seo']['htmlAttrs'] = array_merge([ + 'lang' => $language->getLocale()->getLanguageCode(), + 'dir' => $language->getLocale()->isRightToLeftLanguageDirection() ? 'rtl' : null, + ], $htmlTagAttrs); + + if ($bodyTagAttrs !== []) { + $content['seo']['bodyAttrs'] = $bodyTagAttrs; + } + return $content; } @@ -130,4 +153,17 @@ private function setMetaTag(string $type, string $name, string $content, array $ $manager = $this->metaTagRegistry->getManagerForProperty($name); $manager->addProperty($name, $content, $subProperties, $replace, $type); } + + /** + * @codeCoverageIgnore + */ + private function normalizeAttr(array $rawHtmlAttrs): array + { + $htmlAttrs = []; + + foreach ($rawHtmlAttrs as $attr => $value) { + $htmlAttrs[htmlspecialchars((string)$attr)] = htmlspecialchars((string)$value); + } + return $htmlAttrs; + } } diff --git a/Tests/Unit/Event/Listener/AfterCacheableContentIsGeneratedListenerTest.php b/Tests/Unit/Event/Listener/AfterCacheableContentIsGeneratedListenerTest.php index 3c8579d4..854ec25b 100644 --- a/Tests/Unit/Event/Listener/AfterCacheableContentIsGeneratedListenerTest.php +++ b/Tests/Unit/Event/Listener/AfterCacheableContentIsGeneratedListenerTest.php @@ -25,7 +25,11 @@ use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; use TYPO3\CMS\Core\EventDispatcher\ListenerProvider; use TYPO3\CMS\Core\Http\ServerRequest; +use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; +use TYPO3\CMS\Core\TypoScript\AST\Node\RootNode; +use TYPO3\CMS\Core\TypoScript\FrontendTypoScript; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; @@ -128,6 +132,19 @@ public function testModifiedPageTitle(): void $request = $this->prophesize(ServerRequestInterface::class); $request->getAttribute(Argument::is('headless'))->willReturn(new Headless(HeadlessMode::FULL)); + $request->getAttribute(Argument::is('language'))->willReturn(new SiteLanguage( + 0, + 'en', + new Uri('/en'), + [] + )); + + $frontendTyposcript = new FrontendTypoScript(new RootNode(), [], [], []); + $frontendTyposcript->setSetupTree(new RootNode()); + $frontendTyposcript->setSetupArray([]); + + $request->getAttribute(Argument::is('frontend.typoscript'))->willReturn($frontendTyposcript); + $controller = $this->prophesize(TypoScriptFrontendController::class); $controller->content = json_encode(['meta' => ['title' => 'test before event'], 'seo' => ['title' => 'test before event']]); $controller->cObj = $this->prophesize(ContentObjectRenderer::class)->reveal(); @@ -137,7 +154,7 @@ public function testModifiedPageTitle(): void $listener($event); - self::assertSame(json_encode(['meta' => ['title' => 'test before event'], 'seo' => ['title' => 'Modified title via PageTitleProviderManager', 'meta' => []]]), $event->getController()->content); + self::assertSame(json_encode(['meta' => ['title' => 'test before event'], 'seo' => ['title' => 'Modified title via PageTitleProviderManager', 'meta' => [], 'htmlAttrs' => ['lang' => 'en', 'dir' => null]]]), $event->getController()->content); } public function testHreflangs(): void @@ -158,6 +175,19 @@ public function testHreflangs(): void $request = $this->prophesize(ServerRequestInterface::class); $request->getAttribute(Argument::is('headless'))->willReturn(new Headless(HeadlessMode::FULL)); + $request->getAttribute(Argument::is('language'))->willReturn(new SiteLanguage( + 0, + 'en', + new Uri('/en'), + [] + )); + + $frontendTyposcript = new FrontendTypoScript(new RootNode(), [], [], []); + $frontendTyposcript->setSetupTree(new RootNode()); + $frontendTyposcript->setSetupArray([]); + + $request->getAttribute(Argument::is('frontend.typoscript'))->willReturn($frontendTyposcript); + $GLOBALS['TYPO3_REQUEST'] = $request->reveal(); $controller = $this->prophesize(TypoScriptFrontendController::class); $controller->content = json_encode(['meta' => ['title' => 'test before event'], 'seo' => ['title' => 'test before event']]); @@ -184,6 +214,6 @@ public function handle(): void {} ['rel' => 'alternate', 'hreflang' => 'pl-PL', 'href' => 'https://example.com/pl'], ['rel' => 'alternate', 'hreflang' => 'en-US', 'href' => 'https://example.com/us'], ['rel' => 'alternate', 'hreflang' => 'en-UK', 'href' => 'https://example.com/uk'], - ]]]), $event->getController()->content); + ], 'htmlAttrs' => ['lang' => 'en', 'dir' => null]]]), $event->getController()->content); } } From 6b52c903bbc7fc7f27740f2ba802d4540deba80c Mon Sep 17 00:00:00 2001 From: Oliver Kroener Date: Sat, 8 Feb 2025 17:36:53 +0000 Subject: [PATCH 6/7] [TASK] Fixed code --- Classes/DataProcessing/DataProcessingTrait.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Classes/DataProcessing/DataProcessingTrait.php b/Classes/DataProcessing/DataProcessingTrait.php index cc8f77f1..bf9b0242 100644 --- a/Classes/DataProcessing/DataProcessingTrait.php +++ b/Classes/DataProcessing/DataProcessingTrait.php @@ -27,20 +27,22 @@ protected function removeDataIfnotAppendInConfiguration(array $processorConfigur // Items to keep $removeAll = !isset($processorConfiguration['appendData']) || $processorConfiguration['appendData'] == 0; $keepItems = $removeAll ? [] : array_flip(array_map('trim', explode(',', $processorConfiguration['appendData']))); - if ($removeAll) + if ($removeAll) { unset($processedData['data']); - else + } else { $processedData['data'] = array_intersect_key($processedData['data'], $keepItems); + } if ( isset($processorConfiguration['as'], $processedData[$processorConfiguration['as']]) && is_array($processedData[$processorConfiguration['as']]) ) { foreach ($processedData[$processorConfiguration['as']] as &$item) { if (is_array($item) && isset($item['data'])) { - if ($removeAll) + if ($removeAll) { unset($item['data']); - else + } else { $item['data'] = array_intersect_key($item['data'], $keepItems); + } } if ($this->isMenuProcessor() && isset($item['children']) && is_array($item['children'])) { @@ -69,10 +71,11 @@ protected function isMenuProcessor(): bool private function removeDataInChildrenNodes(array &$children, bool $removeAll, array $keepItems, string $nodeName = 'children'): void { foreach ($children as &$childrenItem) { - if ($removeAll) + if ($removeAll) { unset($childrenItem['data']); - else + } else { $childrenItem['data'] = array_intersect_key($childrenItem['data'], $keepItems); + } if (isset($childrenItem[$nodeName]) && is_array($childrenItem[$nodeName])) { $this->removeDataInChildrenNodes($childrenItem[$nodeName], $nodeName); } From 4663311e80b80dfc41a711f7de6450467e9769db Mon Sep 17 00:00:00 2001 From: twoldanski <66474451+twoldanski@users.noreply.github.com> Date: Tue, 28 Jan 2025 12:30:45 +0100 Subject: [PATCH 7/7] [BUGFIX] Generate proper hash for v13 in LoginFormViewHelper (#804) Resolves: #798 --- Classes/ViewHelpers/LoginFormViewHelper.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Classes/ViewHelpers/LoginFormViewHelper.php b/Classes/ViewHelpers/LoginFormViewHelper.php index b50b7199..25941d7b 100644 --- a/Classes/ViewHelpers/LoginFormViewHelper.php +++ b/Classes/ViewHelpers/LoginFormViewHelper.php @@ -19,9 +19,7 @@ use TYPO3\CMS\Core\Security\RequestToken; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject; - use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy; - use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper; use function base64_encode; @@ -183,11 +181,17 @@ protected function renderHiddenReferrerFields(): string ); $this->addHiddenField( '__referrer[arguments]', - $this->hashService->appendHmac(base64_encode(serialize($request->getArguments()))) + $this->hashService->appendHmac( + base64_encode(serialize($request->getArguments())), + class_exists(\TYPO3\CMS\Extbase\Security\HashScope::class) ? \TYPO3\CMS\Extbase\Security\HashScope::class::ReferringArguments->prefix() : '' + ) ); $this->addHiddenField( '__referrer[@request]', - $this->hashService->appendHmac(json_encode($actionRequest)) + $this->hashService->appendHmac( + json_encode($actionRequest), + class_exists(\TYPO3\CMS\Extbase\Security\HashScope::class) ? \TYPO3\CMS\Extbase\Security\HashScope::class::ReferringRequest->prefix() : '' + ) ); return '';