Skip to content

Commit 41e2907

Browse files
committed
[FEATURE] add ContentAreaProcessor for TYPO3 v14 f:render.contentArea
1 parent c2b9841 commit 41e2907

4 files changed

Lines changed: 170 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace B13\Container\DataProcessing;
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\Domain\Factory\FrontendContainerFactory;
16+
use B13\Container\Tca\Registry;
17+
use Psr\Log\LoggerInterface;
18+
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
19+
use TYPO3\CMS\Core\Context\Context;
20+
use TYPO3\CMS\Core\Domain\RecordFactory;
21+
use TYPO3\CMS\Core\Information\Typo3Version;
22+
use TYPO3\CMS\Core\Page\ContentArea;
23+
use TYPO3\CMS\Core\Page\ContentAreaClosure;
24+
use TYPO3\CMS\Core\Page\ContentAreaCollection;
25+
use TYPO3\CMS\Core\Page\ContentSlideMode;
26+
use TYPO3\CMS\Core\Utility\GeneralUtility;
27+
use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor;
28+
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
29+
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
30+
31+
use function array_map;
32+
33+
/**
34+
* Automatically detects if content element has container columns
35+
* adds them lazily to the content variable.
36+
* The ContentArea can be used in f:render.contentArea ViewHelper
37+
*
38+
* Only use this DataProcessor for TYPO3 v14 or higher:
39+
*
40+
* typoscript:
41+
* lib.contentElement.dataProcessing.1773665522 = B13\Container\DataProcessing\ContentAreaProcessor
42+
* #or
43+
* tt_content.b13-2cols < lib.contentElement
44+
* tt_content.b13-2cols {
45+
* templateName = 2Cols
46+
* templateRootPaths.10 = EXT:base/Resources/Private/Templates
47+
* dataProcessing.100 = B13\Container\DataProcessing\ContentAreaProcessor
48+
* }
49+
*
50+
* html:
51+
* <f:render.contentArea contentArea="{content.200}" />
52+
*
53+
*/
54+
#[Autoconfigure(public: true)]
55+
readonly class ContentAreaProcessor implements DataProcessorInterface
56+
{
57+
58+
public function __construct(
59+
protected ContentDataProcessor $contentDataProcessor,
60+
protected Context $context,
61+
protected FrontendContainerFactory $frontendContainerFactory,
62+
protected Registry $tcaRegistry,
63+
protected RecordFactory $recordFactory,
64+
protected Typo3Version $typo3Version,
65+
protected LoggerInterface $logger,
66+
) {}
67+
68+
public function process(
69+
ContentObjectRenderer $cObj,
70+
array $contentObjectConfiguration,
71+
array $processorConfiguration,
72+
array $processedData,
73+
): array {
74+
if (((float)$this->typo3Version->getBranch()) <= 14.1) {
75+
$this->logger->error(ContentAreaProcessor::class . ' requires TYPO3 v14.2 or higher. Please check your configuration.');
76+
77+
return $processedData;
78+
}
79+
80+
$record = $cObj->data;
81+
82+
$CType = $record['CType'] ?? '';
83+
if (!$this->tcaRegistry->isContainerElement($CType)) {
84+
return $processedData;
85+
}
86+
87+
$columnsColPos = $this->tcaRegistry->getAllAvailableColumnsColPos($CType);
88+
89+
$container = null;
90+
91+
$areas = [];
92+
foreach ($columnsColPos as $colPos) {
93+
$areas[$colPos] = new ContentAreaClosure(
94+
function () use (&$container, $CType, $cObj, $record, $colPos): ContentArea {
95+
$container ??= $this->frontendContainerFactory->buildContainer($cObj, $this->context, (int)$record['uid']);
96+
97+
$contentDefenderConfiguration = $this->tcaRegistry->getContentDefenderConfiguration($CType, $colPos);
98+
99+
$rows = $container->getChildrenByColPos($colPos);
100+
101+
$records = array_map(fn($row) => $this->recordFactory->createFromDatabaseRow('tt_content', $row), $rows);
102+
return new ContentArea(
103+
(string)$colPos,
104+
$this->tcaRegistry->getColPosName($record['CType'], $colPos),
105+
$colPos,
106+
ContentSlideMode::None,
107+
GeneralUtility::trimExplode(',', $contentDefenderConfiguration['allowedContentTypes'] ?? '', true),
108+
GeneralUtility::trimExplode(',', $contentDefenderConfiguration['disallowedContentTypes'] ?? '', true),
109+
[
110+
'container' => $container,
111+
],
112+
$records,
113+
);
114+
},
115+
);
116+
}
117+
118+
$processedData[$processedConfiguration['as'] ?? 'content'] = new ContentAreaCollection($areas);
119+
return $processedData;
120+
}
121+
}

Classes/Tca/Registry.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ public function getContainerLabel(string $cType): string
187187
return $GLOBALS['TCA']['tt_content']['containerConfiguration'][$cType]['label'] ?? $cType;
188188
}
189189

190+
public function getColPosName(string $cType, int $colPos): ?string
191+
{
192+
$grid = $this->getGrid($cType);
193+
foreach ($grid as $row) {
194+
foreach ($row as $column) {
195+
if ($column['colPos'] === $colPos) {
196+
return (string)$column['name'];
197+
}
198+
}
199+
}
200+
return null;
201+
}
202+
190203
public function getAvailableColumns(string $cType): array
191204
{
192205
$columns = [];

Tests/Functional/Frontend/DefaultLanguageTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use PHPUnit\Framework\Attributes\Group;
1414
use PHPUnit\Framework\Attributes\Test;
15+
use TYPO3\CMS\Core\Information\Typo3Version;
1516
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
1617

1718
class DefaultLanguageTest extends AbstractFrontend
@@ -39,6 +40,33 @@ public function childrenAreRendered(): void
3940
self::assertStringContainsString('<h2 class="">left-side-default</h2>', $body);
4041
}
4142

43+
#[Test]
44+
#[Group('frontend')]
45+
public function childrenAreRenderedContentArea(): void
46+
{
47+
if (((float)(new Typo3Version())->getBranch()) < 14.2) {
48+
$this->markTestSkipped('Content area rendering is only supported in TYPO3 v14.2 and above');
49+
}
50+
51+
$this->importCSVDataSet(__DIR__ . '/Fixtures/default_language_ContentArea.csv');
52+
$this->setUpFrontendRootPage(
53+
1,
54+
[
55+
'constants' => ['EXT:container/Tests/Functional/Frontend/Fixtures/TypoScript/constants.typoscript'],
56+
'setup' => [
57+
'EXT:container/Tests/Functional/Frontend/Fixtures/TypoScript/setup.typoscript',
58+
'EXT:container_example/Configuration/Sets/ContainerExample/TypoScript/2ColsContentArea/setup.typoscript',
59+
],
60+
]
61+
);
62+
$response = $this->executeFrontendRequestWrapper(new InternalRequest('http://localhost/'));
63+
$body = (string)$response->getBody();
64+
$body = $this->prepareContent($body);
65+
// rendered content
66+
self::assertStringContainsString('<h2 class="">left-side-default</h2>', $body);
67+
self::assertStringContainsString('<h2 class="">right-side-default</h2>', $body);
68+
}
69+
4270
#[Test]
4371
#[Group('frontend')]
4472
public function childrenAreRenderedAsSorted(): void
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"pages"
2+
,"uid","pid","title","slug"
3+
,1,0,"page-1","/"
4+
"tt_content"
5+
,"uid","pid","CType","header","sorting","sys_language_uid","colPos","tx_container_parent"
6+
,1,1,"b13-2cols-content-area","container-default",256,0,,
7+
,2,1,"header","left-side-default",128,0,200,1
8+
,3,1,"header","right-side-default",64,0,201,1

0 commit comments

Comments
 (0)