From 22c8ee04a35d2f56847230aa90a9b8d302fab7d1 Mon Sep 17 00:00:00 2001 From: Nikita Hovratov Date: Sun, 17 Nov 2024 17:46:13 +0100 Subject: [PATCH 1/2] [FEATURE] Enable control over header and footer backend preview This patch adds the possibility to override the standard header and footer of Content Element previews. Before it was only possible to add a preview for the content area. For this to work, a new layout "Preview" has been added, which all previews should include. This layout renders three sections: Header, Content and Footer. One may implement any of these sections. Omitting a section will result to a fallback to the default preview rendering of TYPO3. It is possible to effectively disable a preview section, by rendering an empty preview. The old preview without a layout still works, but is now deprecated and will log a deprecation level log entry. Users are advised to migrate now. @todo - Adjust documentation - Adjust kickstart backend-preview - Document deprecation and migration Fixes: #275 --- .../accordion/templates/backend-preview.html | 23 ++-- .../card_group/templates/backend-preview.html | 75 +++++------ .../cta/templates/backend-preview.html | 17 ++- .../icon_group/templates/backend-preview.html | 47 +++---- .../templates/backend-preview.html | 15 ++- .../tabs/templates/backend-preview.html | 6 +- Classes/Backend/Preview/PreviewRenderer.php | 121 +++++++++++++----- .../Layouts/Preview/Content/Preview.html | 1 + .../Layouts/Preview/Footer/Preview.html | 1 + .../Layouts/Preview/Header/Preview.html | 1 + 10 files changed, 197 insertions(+), 110 deletions(-) create mode 100644 Resources/Private/Layouts/Preview/Content/Preview.html create mode 100644 Resources/Private/Layouts/Preview/Footer/Preview.html create mode 100644 Resources/Private/Layouts/Preview/Header/Preview.html diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/accordion/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/accordion/templates/backend-preview.html index 94a122f0..53562822 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/accordion/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/accordion/templates/backend-preview.html @@ -5,16 +5,19 @@ data-namespace-typo3-fluid="true" > - + -
- - -
- {item.text} -
-
-
-
+ + +
+ + +
+ {item.text} +
+
+
+
+
diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/card_group/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/card_group/templates/backend-preview.html index 68752dac..706ad208 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/card_group/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/card_group/templates/backend-preview.html @@ -5,48 +5,51 @@ data-namespace-typo3-fluid="true" > - + -
- -
- + + +
+ +
+ -
- -
- -
- -
-
-
-

- {item.title} -

- - {item.bodytext} +
+ +
+ +
+ +
-
- - - - {item.link_title} - +
+

+ {item.title} +

+ + {item.bodytext} - - + + + + {item.link_title} + + + + - -
-
-
- -
- -
+
+ +
+ +
+
+
+
+
+ diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/cta/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/cta/templates/backend-preview.html index 8b74177b..7a8207c0 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/cta/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/cta/templates/backend-preview.html @@ -4,12 +4,17 @@ data-namespace-typo3-fluid="true" > - + -
-

{data.header}

- {data.bodytext} - {data.link_title} -
+ + + + +
+

{data.header}

+ {data.bodytext} + {data.link_title} +
+
diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/icon_group/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/icon_group/templates/backend-preview.html index a2dc668d..15f7e05b 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/icon_group/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/icon_group/templates/backend-preview.html @@ -4,30 +4,33 @@ data-namespace-typo3-fluid="true" > - + -
- -
- -
- -
- -
-
-
-

- {item.title} -

- - {item.bodytext} + + +
+ +
+ +
+ +
+ +
+
+

+ {item.title} +

+ + {item.bodytext} + +
-
- -
-
-
+ +
+ +
+ diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/imageslider/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/imageslider/templates/backend-preview.html index 4ddd638e..42ae9374 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/imageslider/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/imageslider/templates/backend-preview.html @@ -4,12 +4,15 @@ data-namespace-typo3-fluid="true" > - + -
- - - -
+ + +
+ + + +
+
diff --git a/Build/content-blocks-examples/ContentBlocks/ContentElements/tabs/templates/backend-preview.html b/Build/content-blocks-examples/ContentBlocks/ContentElements/tabs/templates/backend-preview.html index 13356062..a3846d64 100644 --- a/Build/content-blocks-examples/ContentBlocks/ContentElements/tabs/templates/backend-preview.html +++ b/Build/content-blocks-examples/ContentBlocks/ContentElements/tabs/templates/backend-preview.html @@ -3,6 +3,10 @@ data-namespace-typo3-fluid="true" > - + + + + + diff --git a/Classes/Backend/Preview/PreviewRenderer.php b/Classes/Backend/Preview/PreviewRenderer.php index 98fce0b2..1413f4bd 100644 --- a/Classes/Backend/Preview/PreviewRenderer.php +++ b/Classes/Backend/Preview/PreviewRenderer.php @@ -29,6 +29,8 @@ use TYPO3\CMS\Core\View\ViewFactoryData; use TYPO3\CMS\Core\View\ViewFactoryInterface; use TYPO3\CMS\Core\View\ViewInterface; +use TYPO3Fluid\Fluid\View\Exception\InvalidSectionException; +use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException; /** * Sets up Fluid and applies the same DataProcessor as the frontend to the data record. @@ -46,50 +48,77 @@ public function __construct( protected ViewFactoryInterface $viewFactory, ) {} + public function renderPageModulePreviewHeader(GridColumnItem $item): string + { + if (!$this->hasPreviewLayout($item)) { + return parent::renderPageModulePreviewHeader($item); + } + try { + $preview = $this->renderPreview($item, 'Header'); + } catch (InvalidSectionException|InvalidTemplateResourceException) { + return parent::renderPageModulePreviewHeader($item); + } + return $preview; + } + public function renderPageModulePreviewContent(GridColumnItem $item): string + { + if (!$this->hasPreviewLayout($item)) { + $template = $this->getContentBlockTemplatePath($item) . '/' . ContentBlockPathUtility::getBackendPreviewFileName(); + trigger_error( + 'The Content Blocks preview template "' . $template . '" should be migrated to use the Preview layout.', + E_USER_DEPRECATED + ); + } + try { + $preview = $this->renderPreview($item, 'Content'); + } catch (InvalidSectionException|InvalidTemplateResourceException) { + return parent::renderPageModulePreviewContent($item); + } + return $preview; + } + + public function renderPageModulePreviewFooter(GridColumnItem $item): string + { + if (!$this->hasPreviewLayout($item)) { + return parent::renderPageModulePreviewFooter($item); + } + try { + $preview = $this->renderPreview($item, 'Footer'); + } catch (InvalidSectionException|InvalidTemplateResourceException) { + return parent::renderPageModulePreviewFooter($item); + } + return $preview; + } + + protected function renderPreview(GridColumnItem $item, string $section): string { /** @var ServerRequestInterface $request */ $request = $GLOBALS['TYPO3_REQUEST']; $record = $item->getRecord(); - $recordType = $item->getRecordType(); $table = $item->getTable(); - $tableDefinition = $this->tableDefinitionCollection->getTable($table); - $contentTypeCollection = $tableDefinition->getContentTypeDefinitionCollection(); - if ($contentTypeCollection->hasType($recordType)) { - $contentTypeDefinition = $contentTypeCollection->getType($recordType); - } else { - $contentTypeDefinition = $contentTypeCollection->getFirst(); - } - $contentBlockExtPath = $this->contentBlockRegistry->getContentBlockExtPath($contentTypeDefinition->getName()); - $contentBlockPrivatePath = $contentBlockExtPath . '/' . ContentBlockPathUtility::getTemplatesFolder(); - - // Fall back to standard preview rendering if EditorPreview.html does not exist. - $editorPreviewExtPath = $contentBlockExtPath . '/' . ContentBlockPathUtility::getBackendPreviewPath(); - $editorPreviewAbsPath = GeneralUtility::getFileAbsFileName($editorPreviewExtPath); - if (!file_exists($editorPreviewAbsPath)) { - $result = parent::renderPageModulePreviewContent($item); - return $result; - } $resolvedRecord = $this->recordFactory->createResolvedRecordFromDatabaseRow($table, $record); $data = $this->contentBlockDataDecorator->decorate($resolvedRecord, $item->getContext()); - $view = $this->createView($contentBlockPrivatePath, $request, $item); + $view = $this->createView($request, $item, $section); $view->assign('data', $data); $result = $view->render(); + $result = trim($result); return $result; } protected function createView( - string $contentBlockPrivatePath, ServerRequestInterface $request, - GridColumnItem $item + GridColumnItem $item, + string $section, ): ViewInterface { + $templatePath = $this->getContentBlockTemplatePath($item); $pageUid = $item->getContext()->getPageId(); - $partialRootPaths = $this->getContentBlocksPartialRootPaths($contentBlockPrivatePath, $pageUid); - $layoutRootPaths = $this->getContentBlocksLayoutRootPaths($contentBlockPrivatePath, $pageUid); + $partialRootPaths = $this->getContentBlocksPartialRootPaths($templatePath, $pageUid); + $layoutRootPaths = $this->getContentBlocksLayoutRootPaths($templatePath, $pageUid, $section); $viewFactoryData = new ViewFactoryData( partialRootPaths: $partialRootPaths, layoutRootPaths: $layoutRootPaths, - templatePathAndFilename: $contentBlockPrivatePath . '/' . ContentBlockPathUtility::getBackendPreviewFileName(), + templatePathAndFilename: $templatePath . '/' . ContentBlockPathUtility::getBackendPreviewFileName(), request: $request ); return $this->viewFactory->create($viewFactoryData); @@ -98,14 +127,14 @@ protected function createView( /** * @return array */ - protected function getContentBlocksPartialRootPaths(string $contentBlockPrivatePath, int $pageUid): array + protected function getContentBlocksPartialRootPaths(string $templatePath, int $pageUid): array { $contentBlockPartialRootPaths = $this->rootPathsSettings->getContentBlocksPartialRootPaths($pageUid); $partialRootPaths = [ 'EXT:backend/Resources/Private/Partials/', 'EXT:content_blocks/Resources/Private/Partials/', ...$contentBlockPartialRootPaths, - $contentBlockPrivatePath . '/partials/', + $templatePath . '/partials/', ]; return $partialRootPaths; } @@ -113,10 +142,44 @@ protected function getContentBlocksPartialRootPaths(string $contentBlockPrivateP /** * @return array */ - protected function getContentBlocksLayoutRootPaths(string $contentBlockPrivatePath, int $pageUid): array + protected function getContentBlocksLayoutRootPaths(string $templatePath, int $pageUid, string $section): array { - $layoutRootPaths = $this->rootPathsSettings->getContentBlocksLayoutRootPaths($pageUid); - $layoutRootPaths[] = $contentBlockPrivatePath . '/layouts/'; + $layoutRootPaths = [ + 'EXT:content_blocks/Resources/Private/Layouts/Preview/' . $section, + ...$this->rootPathsSettings->getContentBlocksLayoutRootPaths($pageUid), + $templatePath . '/layouts/' + ]; return $layoutRootPaths; } + + protected function getContentBlockTemplatePath(GridColumnItem $item): string + { + $recordType = $item->getRecordType(); + $table = $item->getTable(); + $tableDefinition = $this->tableDefinitionCollection->getTable($table); + $contentTypeCollection = $tableDefinition->getContentTypeDefinitionCollection(); + if ($contentTypeCollection->hasType($recordType)) { + $contentTypeDefinition = $contentTypeCollection->getType($recordType); + } else { + $contentTypeDefinition = $contentTypeCollection->getFirst(); + } + $contentBlockExtPath = $this->contentBlockRegistry->getContentBlockExtPath($contentTypeDefinition->getName()); + $contentBlockPrivatePath = $contentBlockExtPath . '/' . ContentBlockPathUtility::getTemplatesFolder(); + return $contentBlockPrivatePath; + } + + /** + * @deprecated Remove in Content Blocks v2.0 + */ + protected function hasPreviewLayout(GridColumnItem $item): bool + { + $templatePath = $this->getContentBlockTemplatePath($item); + $templatePathAndFilename = $templatePath . '/' . ContentBlockPathUtility::getBackendPreviewFileName(); + $absoluteTemplatePath = GeneralUtility::getFileAbsFileName($templatePathAndFilename); + if (!file_exists($absoluteTemplatePath)) { + return false; + } + $contents = file_get_contents($absoluteTemplatePath); + return str_contains($contents, ' diff --git a/Resources/Private/Layouts/Preview/Footer/Preview.html b/Resources/Private/Layouts/Preview/Footer/Preview.html new file mode 100644 index 00000000..04db17c6 --- /dev/null +++ b/Resources/Private/Layouts/Preview/Footer/Preview.html @@ -0,0 +1 @@ + diff --git a/Resources/Private/Layouts/Preview/Header/Preview.html b/Resources/Private/Layouts/Preview/Header/Preview.html new file mode 100644 index 00000000..f61cfcb6 --- /dev/null +++ b/Resources/Private/Layouts/Preview/Header/Preview.html @@ -0,0 +1 @@ + From 0bb9e980d4aa2176389da130ad05277fe8d81e85 Mon Sep 17 00:00:00 2001 From: Nikita Hovratov Date: Sun, 17 Nov 2024 17:49:24 +0100 Subject: [PATCH 2/2] Fix cgl --- Classes/Backend/Preview/PreviewRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Backend/Preview/PreviewRenderer.php b/Classes/Backend/Preview/PreviewRenderer.php index 1413f4bd..0eaf6263 100644 --- a/Classes/Backend/Preview/PreviewRenderer.php +++ b/Classes/Backend/Preview/PreviewRenderer.php @@ -147,7 +147,7 @@ protected function getContentBlocksLayoutRootPaths(string $templatePath, int $pa $layoutRootPaths = [ 'EXT:content_blocks/Resources/Private/Layouts/Preview/' . $section, ...$this->rootPathsSettings->getContentBlocksLayoutRootPaths($pageUid), - $templatePath . '/layouts/' + $templatePath . '/layouts/', ]; return $layoutRootPaths; }