Skip to content

Commit dfc61c4

Browse files
committed
[BUGFIX] custom backend templates
the container grid is not longer rendered after the content of the StandardContentRenderer but is assign as a variable "tx_container_grid" to the record. this variable is used in a backend template: <f:format.raw>{tx_container_grid}</f:format.raw> container shipped the default backend template (again, we has dropped this in EXT:container 3.0) in v12 and v13 the ContainerPreviewRenderer is no longer used we use PageContentPreviewRendering Event Listener instead of the ContainerPreviewRenderer the Listener assigns the container grid to tx_container_grid in v11 the PreviewRenderer is still used but assigns the grid as a variable instead of rendering the grid after the content background: in v13 the Event Listener "FluidBasedContentPreviewRenderer" was introduced for rendering backend templates and "disables" StandardContentRenderer if a backend template is defined. in addition with this change you can also render custom stuff in you backend template after the grid, which was not possible before Fixes: #563
1 parent 9d29e35 commit dfc61c4

File tree

11 files changed

+197
-117
lines changed

11 files changed

+197
-117
lines changed

Build/phpstan11-7.4.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ parameters:
1515
- %currentWorkingDirectory%/Tests/Functional/Listener/ContentUsedOnPageTest.php
1616
- %currentWorkingDirectory%/Classes/Listener/RecordSummaryForLocalization.php
1717
- %currentWorkingDirectory%/Classes/Listener/PageTsConfig.php
18+
- %currentWorkingDirectory%/Classes/Listener/PageContentPreviewRendering.php
1819

Build/phpstan11.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ parameters:
1515
- %currentWorkingDirectory%/Tests/Functional/Listener/ContentUsedOnPageTest.php
1616
- %currentWorkingDirectory%/Classes/Listener/RecordSummaryForLocalization.php
1717
- %currentWorkingDirectory%/Classes/Listener/PageTsConfig.php
18+
- %currentWorkingDirectory%/Classes/Listener/PageContentPreviewRendering.php
1819

Classes/Backend/Preview/ContainerPreviewRenderer.php

Lines changed: 7 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,122 +12,23 @@
1212
* of the License, or any later version.
1313
*/
1414

15-
use B13\Container\Backend\Grid\ContainerGridColumn;
16-
use B13\Container\Backend\Grid\ContainerGridColumnItem;
17-
use B13\Container\Backend\Service\NewContentUrlBuilder;
18-
use B13\Container\Domain\Factory\Exception;
19-
use B13\Container\Domain\Factory\PageView\Backend\ContainerFactory;
20-
use B13\Container\Events\BeforeContainerPreviewIsRenderedEvent;
21-
use B13\Container\Tca\Registry;
22-
use Psr\EventDispatcher\EventDispatcherInterface;
15+
2316
use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer;
24-
use TYPO3\CMS\Backend\Utility\BackendUtility;
25-
use TYPO3\CMS\Backend\View\BackendLayout\Grid\Grid;
2617
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
27-
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridRow;
28-
use TYPO3\CMS\Core\Utility\GeneralUtility;
29-
use TYPO3\CMS\Fluid\View\StandaloneView;
3018

3119
class ContainerPreviewRenderer extends StandardContentPreviewRenderer
3220
{
33-
/**
34-
* @var Registry
35-
*/
36-
protected $tcaRegistry;
37-
38-
/**
39-
* @var ContainerFactory
40-
*/
41-
protected $containerFactory;
42-
43-
protected NewContentUrlBuilder $newContentUrlBuilder;
21+
protected GridRenderer $gridRenderer;
4422

45-
/**
46-
* @var EventDispatcherInterface
47-
*/
48-
protected $eventDispatcher;
49-
50-
public function __construct(
51-
Registry $tcaRegistry,
52-
ContainerFactory $containerFactory,
53-
NewContentUrlBuilder $newContentUrlBuilder,
54-
EventDispatcherInterface $eventDispatcher
55-
) {
56-
$this->eventDispatcher = $eventDispatcher;
57-
$this->tcaRegistry = $tcaRegistry;
58-
$this->containerFactory = $containerFactory;
59-
$this->newContentUrlBuilder = $newContentUrlBuilder;
23+
public function __construct(GridRenderer $gridRenderer) {
24+
$this->gridRenderer = $gridRenderer;
6025
}
6126

6227
public function renderPageModulePreviewContent(GridColumnItem $item): string
6328
{
64-
$content = parent::renderPageModulePreviewContent($item);
65-
$context = $item->getContext();
6629
$record = $item->getRecord();
67-
$grid = GeneralUtility::makeInstance(Grid::class, $context);
68-
try {
69-
$container = $this->containerFactory->buildContainer((int)$record['uid']);
70-
} catch (Exception $e) {
71-
// not a container
72-
return $content;
73-
}
74-
$containerGrid = $this->tcaRegistry->getGrid($record['CType']);
75-
foreach ($containerGrid as $cols) {
76-
$rowObject = GeneralUtility::makeInstance(GridRow::class, $context);
77-
foreach ($cols as $col) {
78-
$defVals = $this->getDefValsForContentDefenderAllowsOnlyOneSpecificContentType($record['CType'], (int)$col['colPos']);
79-
$url = $this->newContentUrlBuilder->getNewContentUrlAtTopOfColumn($context, $container, (int)$col['colPos'], $defVals);
80-
$columnObject = GeneralUtility::makeInstance(ContainerGridColumn::class, $context, $col, $container, $url, $defVals !== null);
81-
$rowObject->addColumn($columnObject);
82-
if (isset($col['colPos'])) {
83-
$records = $container->getChildrenByColPos($col['colPos']);
84-
foreach ($records as $contentRecord) {
85-
$url = $this->newContentUrlBuilder->getNewContentUrlAfterChild($context, $container, (int)$col['colPos'], (int)$contentRecord['uid'], $defVals);
86-
$columnItem = GeneralUtility::makeInstance(ContainerGridColumnItem::class, $context, $columnObject, $contentRecord, $container, $url);
87-
$columnObject->addItem($columnItem);
88-
}
89-
}
90-
}
91-
$grid->addRow($rowObject);
92-
}
93-
94-
$gridTemplate = $this->tcaRegistry->getGridTemplate($record['CType']);
95-
$partialRootPaths = $this->tcaRegistry->getGridPartialPaths($record['CType']);
96-
$layoutRootPaths = $this->tcaRegistry->getGridLayoutPaths($record['CType']);
97-
$view = GeneralUtility::makeInstance(StandaloneView::class);
98-
$view->setPartialRootPaths($partialRootPaths);
99-
$view->setLayoutRootPaths($layoutRootPaths);
100-
$view->setTemplatePathAndFilename($gridTemplate);
101-
102-
$view->assign('hideRestrictedColumns', (bool)(BackendUtility::getPagesTSconfig($context->getPageId())['mod.']['web_layout.']['hideRestrictedCols'] ?? false));
103-
$view->assign('newContentTitle', $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:newContentElement'));
104-
$view->assign('newContentTitleShort', $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:content'));
105-
$view->assign('allowEditContent', $this->getBackendUser()->check('tables_modify', 'tt_content'));
106-
// keep compatibility
107-
$view->assign('containerGrid', $grid);
108-
$view->assign('grid', $grid);
109-
$view->assign('containerRecord', $record);
110-
$view->assign('context', $context);
111-
$beforeContainerPreviewIsRendered = new BeforeContainerPreviewIsRenderedEvent($container, $view, $grid, $item);
112-
$this->eventDispatcher->dispatch($beforeContainerPreviewIsRendered);
113-
$rendered = $view->render();
114-
115-
return $content . $rendered;
116-
}
117-
118-
protected function getDefValsForContentDefenderAllowsOnlyOneSpecificContentType(string $cType, int $colPos): ?array
119-
{
120-
$contentDefefenderConfiguration = $this->tcaRegistry->getContentDefenderConfiguration($cType, $colPos);
121-
$allowedCTypes = GeneralUtility::trimExplode(',', $contentDefefenderConfiguration['allowed.']['CType'] ?? '', true);
122-
$allowedListTypes = GeneralUtility::trimExplode(',', $contentDefefenderConfiguration['allowed.']['list_type'] ?? '', true);
123-
if (count($allowedCTypes) === 1) {
124-
if ($allowedCTypes[0] !== 'list') {
125-
return ['CType' => $allowedCTypes[0]];
126-
}
127-
if (count($allowedListTypes) === 1) {
128-
return ['CType' => 'list', 'list_type' => $allowedListTypes[0]];
129-
}
130-
}
131-
return null;
30+
$record['tx_container_grid'] = $this->gridRenderer->renderGrid($record, $item->getContext(), $item);
31+
$item->setRecord($record);
32+
return parent::renderPageModulePreviewContent($item);
13233
}
13334
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace B13\Container\Backend\Preview;
6+
7+
/*
8+
* This file is part of TYPO3 CMS-based extension "container" by b13.
9+
*
10+
* It is free software; you can redistribute it and/or modify it under
11+
* the terms of the GNU General Public License, either version 2
12+
* of the License, or any later version.
13+
*/
14+
15+
use B13\Container\Backend\Grid\ContainerGridColumn;
16+
use B13\Container\Backend\Grid\ContainerGridColumnItem;
17+
use B13\Container\Backend\Service\NewContentUrlBuilder;
18+
use B13\Container\Domain\Factory\Exception;
19+
use B13\Container\Domain\Factory\PageView\Backend\ContainerFactory;
20+
use B13\Container\Events\BeforeContainerPreviewIsRenderedEvent;
21+
use B13\Container\Tca\Registry;
22+
use Psr\EventDispatcher\EventDispatcherInterface;
23+
use TYPO3\CMS\Backend\Utility\BackendUtility;
24+
use TYPO3\CMS\Backend\View\BackendLayout\Grid\Grid;
25+
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
26+
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridRow;
27+
use TYPO3\CMS\Backend\View\PageLayoutContext;
28+
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
29+
use TYPO3\CMS\Core\Localization\LanguageService;
30+
use TYPO3\CMS\Core\Utility\GeneralUtility;
31+
use TYPO3\CMS\Fluid\View\StandaloneView;
32+
33+
class GridRenderer
34+
{
35+
36+
protected Registry $tcaRegistry;
37+
protected ContainerFactory $containerFactory;
38+
protected NewContentUrlBuilder $newContentUrlBuilder;
39+
protected EventDispatcherInterface $eventDispatcher;
40+
41+
public function __construct(
42+
Registry $tcaRegistry,
43+
ContainerFactory $containerFactory,
44+
NewContentUrlBuilder $newContentUrlBuilder,
45+
EventDispatcherInterface $eventDispatcher
46+
) {
47+
$this->eventDispatcher = $eventDispatcher;
48+
$this->tcaRegistry = $tcaRegistry;
49+
$this->containerFactory = $containerFactory;
50+
$this->newContentUrlBuilder = $newContentUrlBuilder;
51+
}
52+
53+
public function renderGrid(array $record, PageLayoutContext $context, ?GridColumnItem $parentGridColumnItem = null): string
54+
{
55+
$grid = GeneralUtility::makeInstance(Grid::class, $context);
56+
try {
57+
$container = $this->containerFactory->buildContainer((int)$record['uid']);
58+
} catch (Exception $e) {
59+
// not a container
60+
return '';
61+
}
62+
$containerGrid = $this->tcaRegistry->getGrid($record['CType']);
63+
foreach ($containerGrid as $cols) {
64+
$rowObject = GeneralUtility::makeInstance(GridRow::class, $context);
65+
foreach ($cols as $col) {
66+
$defVals = $this->getDefValsForContentDefenderAllowsOnlyOneSpecificContentType($record['CType'], (int)$col['colPos']);
67+
$url = $this->newContentUrlBuilder->getNewContentUrlAtTopOfColumn($context, $container, (int)$col['colPos'], $defVals);
68+
$columnObject = GeneralUtility::makeInstance(ContainerGridColumn::class, $context, $col, $container, $url, $defVals !== null);
69+
$rowObject->addColumn($columnObject);
70+
if (isset($col['colPos'])) {
71+
$records = $container->getChildrenByColPos($col['colPos']);
72+
foreach ($records as $contentRecord) {
73+
$url = $this->newContentUrlBuilder->getNewContentUrlAfterChild($context, $container, (int)$col['colPos'], (int)$contentRecord['uid'], $defVals);
74+
$columnItem = GeneralUtility::makeInstance(ContainerGridColumnItem::class, $context, $columnObject, $contentRecord, $container, $url);
75+
$columnObject->addItem($columnItem);
76+
}
77+
}
78+
}
79+
$grid->addRow($rowObject);
80+
}
81+
82+
$gridTemplate = $this->tcaRegistry->getGridTemplate($record['CType']);
83+
$partialRootPaths = $this->tcaRegistry->getGridPartialPaths($record['CType']);
84+
$layoutRootPaths = $this->tcaRegistry->getGridLayoutPaths($record['CType']);
85+
$view = GeneralUtility::makeInstance(StandaloneView::class);
86+
$view->setPartialRootPaths($partialRootPaths);
87+
$view->setLayoutRootPaths($layoutRootPaths);
88+
$view->setTemplatePathAndFilename($gridTemplate);
89+
90+
$view->assign('hideRestrictedColumns', (bool)(BackendUtility::getPagesTSconfig($context->getPageId())['mod.']['web_layout.']['hideRestrictedCols'] ?? false));
91+
$view->assign('newContentTitle', $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:newContentElement'));
92+
$view->assign('newContentTitleShort', $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:content'));
93+
$view->assign('allowEditContent', $this->getBackendUser()->check('tables_modify', 'tt_content'));
94+
// keep compatibility
95+
$view->assign('containerGrid', $grid);
96+
$view->assign('grid', $grid);
97+
$view->assign('containerRecord', $record);
98+
$view->assign('context', $context);
99+
$beforeContainerPreviewIsRendered = new BeforeContainerPreviewIsRenderedEvent($container, $view, $grid, $parentGridColumnItem);
100+
$this->eventDispatcher->dispatch($beforeContainerPreviewIsRendered);
101+
$rendered = $view->render();
102+
return $rendered;
103+
}
104+
105+
protected function getDefValsForContentDefenderAllowsOnlyOneSpecificContentType(string $cType, int $colPos): ?array
106+
{
107+
$contentDefefenderConfiguration = $this->tcaRegistry->getContentDefenderConfiguration($cType, $colPos);
108+
$allowedCTypes = GeneralUtility::trimExplode(',', $contentDefefenderConfiguration['allowed.']['CType'] ?? '', true);
109+
$allowedListTypes = GeneralUtility::trimExplode(',', $contentDefefenderConfiguration['allowed.']['list_type'] ?? '', true);
110+
if (count($allowedCTypes) === 1) {
111+
if ($allowedCTypes[0] !== 'list') {
112+
return ['CType' => $allowedCTypes[0]];
113+
}
114+
if (count($allowedListTypes) === 1) {
115+
return ['CType' => 'list', 'list_type' => $allowedListTypes[0]];
116+
}
117+
}
118+
return null;
119+
}
120+
121+
protected function getBackendUser(): BackendUserAuthentication
122+
{
123+
return $GLOBALS['BE_USER'];
124+
}
125+
126+
protected function getLanguageService(): LanguageService
127+
{
128+
return $GLOBALS['LANG'];
129+
}
130+
}

Classes/Events/BeforeContainerPreviewIsRenderedEvent.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ final class BeforeContainerPreviewIsRenderedEvent
2525

2626
protected Grid $grid;
2727

28-
protected GridColumnItem $item;
28+
protected ?GridColumnItem $item;
2929

30-
public function __construct(Container $container, StandaloneView $view, Grid $grid, GridColumnItem $item)
30+
public function __construct(Container $container, StandaloneView $view, Grid $grid, ?GridColumnItem $item)
3131
{
3232
$this->container = $container;
3333
$this->view = $view;
@@ -50,7 +50,7 @@ public function getGrid(): Grid
5050
return $this->grid;
5151
}
5252

53-
public function getItem(): GridColumnItem
53+
public function getItem(): ?GridColumnItem
5454
{
5555
return $this->item;
5656
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace B13\Container\Listener;
6+
7+
/*
8+
* This file is part of TYPO3 CMS-based extension "container" by b13.
9+
*
10+
* It is free software; you can redistribute it and/or modify it under
11+
* the terms of the GNU General Public License, either version 2
12+
* of the License, or any later version.
13+
*/
14+
15+
use B13\Container\Backend\Preview\GridRenderer;
16+
use B13\Container\Tca\Registry;
17+
use TYPO3\CMS\Backend\View\Event\PageContentPreviewRenderingEvent;
18+
19+
class PageContentPreviewRendering
20+
{
21+
protected GridRenderer $gridRenderer;
22+
protected Registry $tcaRegistry;
23+
24+
public function __construct(GridRenderer $gridRenderer, Registry $tcaRegistry)
25+
{
26+
$this->gridRenderer = $gridRenderer;
27+
$this->tcaRegistry = $tcaRegistry;
28+
}
29+
30+
public function __invoke(PageContentPreviewRenderingEvent $event): void
31+
{
32+
$record = $event->getRecord();
33+
if (!$this->tcaRegistry->isContainerElement($record['CType'])) {
34+
return;
35+
}
36+
$record['tx_container_grid'] = $this->gridRenderer->renderGrid($record, $event->getPageLayoutContext());
37+
$event->setRecord($record);
38+
}
39+
}

Classes/Tca/ContainerConfiguration.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ContainerConfiguration
4242
*/
4343
protected $icon = 'EXT:container/Resources/Public/Icons/Extension.svg';
4444

45-
protected ?string $backendTemplate = null;
45+
protected string $backendTemplate = 'EXT:container/Resources/Private/Templates/Container.html';
4646

4747
/**
4848
* @var string
@@ -228,7 +228,7 @@ public function getIcon(): string
228228
return $this->icon;
229229
}
230230

231-
public function getBackendTemplate(): ?string
231+
public function getBackendTemplate(): string
232232
{
233233
return $this->backendTemplate;
234234
}

Classes/Tca/Registry.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@ public function configureContainer(ContainerConfiguration $containerConfiguratio
6464
$containerConfiguration->getGroup(),
6565
]
6666
);
67+
$GLOBALS['TCA']['tt_content']['types'][$containerConfiguration->getCType()]['previewRenderer'] = \B13\Container\Backend\Preview\ContainerPreviewRenderer::class;
6768
}
6869

69-
$GLOBALS['TCA']['tt_content']['types'][$containerConfiguration->getCType()]['previewRenderer'] = \B13\Container\Backend\Preview\ContainerPreviewRenderer::class;
70-
7170
if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() >= 13) {
7271
if (!isset($GLOBALS['TCA']['tt_content']['types'][$containerConfiguration->getCType()]['creationOptions'])) {
7372
$GLOBALS['TCA']['tt_content']['types'][$containerConfiguration->getCType()]['creationOptions'] = [];
@@ -266,12 +265,10 @@ public function getPageTsString(): string
266265
}
267266
$groupedByGroup[$group][$cType] = $containerConfiguration;
268267
}
269-
if ($containerConfiguration['backendTemplate'] !== null) {
270-
$pageTs .= LF . 'mod.web_layout.tt_content.preview {
268+
$pageTs .= LF . 'mod.web_layout.tt_content.preview {
271269
' . $cType . ' = ' . $containerConfiguration['backendTemplate'] . '
272270
}
273271
';
274-
}
275272
}
276273
$typo3Version = GeneralUtility::makeInstance(Typo3Version::class);
277274
if ($typo3Version->getMajorVersion() > 12) {

Configuration/Services.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ services:
6868
tags:
6969
- name: event.listener
7070
identifier: 'tx-container-boot-completed'
71+
B13\Container\Listener\PageContentPreviewRendering:
72+
tags:
73+
- name: event.listener
74+
identifier: 'tx-container-page-content-preview-rendering'
75+
before: 'typo3-backend/fluid-preview/content'
7176
B13\Container\Command\FixLanguageModeCommand:
7277
tags:
7378
- name: 'console.command'

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ This is an example to create a 2 column container. The code snippet goes into a
7171
| Method name | Description | Parameters | Default |
7272
| ----------- | ----------- | ---------- | ---------- |
7373
| `setIcon` | icon file, or existing icon identifier | `string $icon` | `'EXT:container/Resources/Public/Icons/Extension.svg'` |
74-
| `setBackendTemplate` | Template for backend view| `string $backendTemplate` | `null'` |
74+
| `setBackendTemplate` | Template for backend view| `string $backendTemplate` | `EXT:container/Resources/Private/Templates/Container.html'` |
7575
| `setGridTemplate` | Template for grid | `string $gridTemplate` | `'EXT:container/Resources/Private/Templates/Container.html'` |
7676
| `setGridPartialPaths` / `addGridPartialPath` | Partial root paths for grid | `array $gridPartialPaths` / `string $gridPartialPath` | `['EXT:backend/Resources/Private/Partials/', 'EXT:container/Resources/Private/Partials/']` |
7777
| `setGridLayoutPaths` | Layout root paths for grid | `array $gridLayoutPaths` | `[]` |

0 commit comments

Comments
 (0)