Skip to content

Commit c323dd9

Browse files
committed
[EXPERIMENTAL] Integrate with EXT:content_blocks
WIP: Move the new rendering logic to WEBCOMPONENT_CONTENTBLOCK cObj and restore previous behaviour
1 parent 71cab95 commit c323dd9

11 files changed

Lines changed: 200 additions & 56 deletions

File tree

Classes/ContentObject/WebcomponentContentObject.php

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Sinso\Webcomponents\DataProviding\AssertionFailedException;
88
use Sinso\Webcomponents\DataProviding\Traits\RenderComponent;
9+
use Sinso\Webcomponents\Dto\Events\ComponentFolderIsApplied;
910
use Sinso\Webcomponents\Dto\Events\ComponentWillBeRendered;
1011
use Sinso\Webcomponents\Dto\ComponentRenderingData;
1112
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
@@ -16,38 +17,26 @@ class WebcomponentContentObject extends AbstractContentObject
1617
{
1718
use RenderComponent;
1819

20+
public function __construct(
21+
private readonly EventDispatcher $eventDispatcher,
22+
) {
23+
}
24+
1925
public function render($conf = []): string
2026
{
2127
$componentRenderingData = GeneralUtility::makeInstance(ComponentRenderingData::class);
2228
if ($this->cObj->getCurrentTable() === 'tt_content') {
2329
$componentRenderingData->setContentRecord($this->cObj->data);
2430
}
25-
if (isset($conf['additionalInputData.'])) {
26-
// apply stdWrap to all additionalInputData properties
27-
foreach ($conf['additionalInputData.'] as $key => $value) {
28-
if (!str_ends_with((string) $key, '.')) {
29-
continue;
30-
}
31-
$keyWithoutDot = substr((string) $key, 0, -1);
32-
$conf['additionalInputData.'][$keyWithoutDot] = $this->cObj->stdWrapValue($keyWithoutDot, $conf['additionalInputData.']);
33-
unset($conf['additionalInputData.'][$key]);
34-
}
35-
$componentRenderingData->setAdditionalInputData($conf['additionalInputData.']);
36-
}
31+
32+
$componentFolder = $this->cObj->stdWrapValue('componentFolder', $conf);
3733
try {
38-
$componentRenderingData = self::evaluateComponent($componentRenderingData, $conf['component'] ?? '', $this->cObj);
34+
$componentRenderingData = $this->applyComponentFolder($componentRenderingData, $componentFolder);
35+
$event = GeneralUtility::makeInstance(ComponentWillBeRendered::class, $this->cObj, $componentRenderingData);
36+
$this->eventDispatcher->dispatch($event);
3937
} catch (AssertionFailedException $e) {
4038
return $e->getRenderingPlaceholder();
4139
}
42-
$componentRenderingData = $this->evaluateTypoScriptConfiguration($componentRenderingData, $conf);
43-
44-
$event = GeneralUtility::makeInstance(ComponentWillBeRendered::class, $this->cObj, $componentRenderingData);
45-
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
46-
$eventDispatcher->dispatch($event);
47-
48-
if (!$componentRenderingData->isRenderable()) {
49-
return '';
50-
}
5140

5241
// render with tag builder
5342
$markup = $this->renderMarkup($componentRenderingData);
@@ -58,19 +47,10 @@ public function render($conf = []): string
5847
return $markup;
5948
}
6049

61-
private function evaluateTypoScriptConfiguration(ComponentRenderingData $componentRenderingData, array $conf): ComponentRenderingData
50+
private function applyComponentFolder(ComponentRenderingData $componentRenderingData, string $componentFolder): ComponentRenderingData
6251
{
63-
if (isset($conf['properties.'])) {
64-
foreach ($conf['properties.'] as $key => $value) {
65-
if (is_array($value)) {
66-
continue;
67-
}
68-
$componentRenderingData->setTagProperty($key, $this->cObj->cObjGetSingle($value, $conf['properties.'][$key . '.']));
69-
}
70-
}
71-
if (($conf['tagName'] ?? '') || ($conf['tagName.'] ?? [])) {
72-
$componentRenderingData->setTagName($this->cObj->stdWrap($conf['tagName'] ?? '', $conf['tagName.'] ?? []) ?: null);
73-
}
52+
$event = GeneralUtility::makeInstance(ComponentFolderIsApplied::class, $componentRenderingData, $this->cObj, $componentFolder);
53+
$this->eventDispatcher->dispatch($event);
7454
return $componentRenderingData;
7555
}
7656

Classes/DataProviding/Traits/Image.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,12 @@
66

77
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
88
use TYPO3\CMS\Core\Resource\FileReference;
9+
use TYPO3\CMS\Core\Utility\GeneralUtility;
910
use TYPO3\CMS\Extbase\Domain\Model\FileReference as ExtbaseFileReference;
1011
use TYPO3\CMS\Extbase\Service\ImageService;
1112

1213
trait Image
1314
{
14-
private ImageService $imageService;
15-
16-
public function injectImageService(ImageService $imageService): void
17-
{
18-
$this->imageService = $imageService;
19-
}
20-
2115
public function getImageUri($image, $width, $height, string $cropVariant = 'default', bool $absolute = false): string
2216
{
2317
if ($image instanceof ExtbaseFileReference) {
@@ -41,7 +35,8 @@ public function getImageUri($image, $width, $height, string $cropVariant = 'defa
4135
$processingInstructions['fileExtension'] = $arguments['fileExtension'];
4236
}
4337

44-
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);
45-
return $this->imageService->getImageUri($processedImage, $absolute);
38+
$imageService = GeneralUtility::makeInstance(ImageService::class);
39+
$processedImage = $imageService->applyProcessingInstructions($image, $processingInstructions);
40+
return $imageService->getImageUri($processedImage, $absolute);
4641
}
4742
}

Classes/DataProviding/Traits/RenderSubComponent.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ protected function renderSubComponent(string $componentClassName, $additionalInp
2929
return null;
3030
}
3131

32-
if (!$componentRenderingData->isRenderable()) {
33-
return null;
34-
}
35-
3632
$properties = $componentRenderingData->getTagProperties();
3733
if ($slot !== null) {
3834
$properties['slot'] = $slot;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sinso\Webcomponents\Dto\Events;
6+
7+
use Sinso\Webcomponents\Dto\ComponentRenderingData;
8+
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
9+
10+
readonly class ComponentFolderIsApplied
11+
{
12+
public function __construct(
13+
public ComponentRenderingData $componentRenderingData,
14+
public ContentObjectRenderer $contentObjectRenderer,
15+
public string $componentFolder,
16+
) {
17+
}
18+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sinso\Webcomponents\EventListener\ComponentFolderIsApplied;
6+
7+
use Praetorius\ViteAssetCollector\Service\ViteService;
8+
use Sinso\Webcomponents\Dto\Events\ComponentFolderIsApplied;
9+
use TYPO3\CMS\Core\Attribute\AsEventListener;
10+
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
11+
12+
#[AsEventListener(identifier: 'webcomponents/addEntryPointToAssetCollector')]
13+
class AddEntryPointToAssetCollector
14+
{
15+
protected const SUPPORTED_ENTRY_POINT_FILE_NAMES = [
16+
'entry.ts',
17+
'entry.js',
18+
];
19+
20+
public function __construct(
21+
private readonly ViteService $viteService
22+
) {
23+
}
24+
25+
public function __invoke(ComponentFolderIsApplied $event): void
26+
{
27+
foreach (self::SUPPORTED_ENTRY_POINT_FILE_NAMES as $fileName) {
28+
$this->findAndAddEntryPoint($event->componentFolder . '/Source/' . $fileName);
29+
}
30+
}
31+
32+
protected function findAndAddEntryPoint(string $entryPointPath): void
33+
{
34+
$absoluteEntryPointFile = ExtensionManagementUtility::resolvePackagePath($entryPointPath);
35+
if (!file_exists($absoluteEntryPointFile)) {
36+
return;
37+
}
38+
39+
$this->viteService->addAssetsFromManifest(
40+
$this->viteService->getDefaultManifestFile(),
41+
$entryPointPath,
42+
);
43+
}
44+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sinso\Webcomponents\EventListener\ComponentFolderIsApplied;
6+
7+
use Sinso\Webcomponents\Dto\Events\ComponentFolderIsApplied;
8+
use TYPO3\CMS\Core\Attribute\AsEventListener;
9+
10+
#[AsEventListener(identifier: 'webcomponents/deriveTagNameFromComponentFolder')]
11+
class DeriveTagNameFromComponentFolder
12+
{
13+
public function __invoke(ComponentFolderIsApplied $event): void
14+
{
15+
$componentFolderPath = rtrim($event->componentFolder, '/');
16+
$lastFolderName = basename($componentFolderPath);
17+
$event->componentRenderingData->setTagName($lastFolderName);
18+
}
19+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sinso\Webcomponents\EventListener\ComponentFolderIsApplied;
6+
7+
use Sinso\Webcomponents\DataProviding\ComponentInterface;
8+
use Sinso\Webcomponents\Dto\Events\ComponentFolderIsApplied;
9+
use TYPO3\CMS\Core\Attribute\AsEventListener;
10+
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
11+
12+
#[AsEventListener(identifier: 'webcomponents/executeProviderFile')]
13+
class ExecuteProviderFile
14+
{
15+
public function __invoke(ComponentFolderIsApplied $event): void
16+
{
17+
$absoluteComponentFolder = ExtensionManagementUtility::resolvePackagePath($event->componentFolder);
18+
$providerFile = $absoluteComponentFolder . '/Source/provide.php';
19+
if (!file_exists($providerFile)) {
20+
return;
21+
}
22+
23+
$class = require $providerFile;
24+
$provider = new $class();
25+
if (!$provider instanceof ComponentInterface) {
26+
throw new \Exception('Provider must implement ComponentInterface', 1722526311);
27+
}
28+
$provider->setContentObjectRenderer($event->contentObjectRenderer);
29+
30+
$provider->provide($event->componentRenderingData);
31+
}
32+
}

Classes/ServiceProvider.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sinso\Webcomponents;
6+
7+
use Psr\Container\ContainerInterface;
8+
use TYPO3\CMS\ContentBlocks\Definition\ContentType\ContentType;
9+
use TYPO3\CMS\ContentBlocks\Definition\TableDefinitionCollection;
10+
use TYPO3\CMS\ContentBlocks\Registry\ContentBlockRegistry;
11+
use TYPO3\CMS\Core\Package\AbstractServiceProvider;
12+
13+
class ServiceProvider extends AbstractServiceProvider
14+
{
15+
16+
protected static function getPackagePath(): string
17+
{
18+
return __DIR__ . '/../';
19+
}
20+
21+
protected static function getPackageName(): string
22+
{
23+
return 'sinso/webcomponents';
24+
}
25+
26+
public function getFactories(): array
27+
{
28+
return [];
29+
}
30+
31+
public function getExtensions(): array
32+
{
33+
return [
34+
'content-blocks.typoscript' => static::overwriteContentBlocksTypoScript(...),
35+
] + parent::getExtensions();
36+
}
37+
38+
public static function overwriteContentBlocksTypoScript(ContainerInterface $container, \ArrayObject $typoScriptArrayObject): \ArrayObject
39+
{
40+
$contentBlockRegistry = $container->get(ContentBlockRegistry::class);
41+
$tableDefinitionCollection = $container->get(TableDefinitionCollection::class);
42+
foreach ($tableDefinitionCollection as $tableDefinition) {
43+
foreach ($tableDefinition->getContentTypeDefinitionCollection() ?? [] as $typeDefinition) {
44+
if ($tableDefinition->getContentType() !== ContentType::CONTENT_ELEMENT) {
45+
continue;
46+
}
47+
$contentBlockExtPath = $contentBlockRegistry->getContentBlockExtPath($typeDefinition->getName());
48+
$typoScript = <<<HEREDOC
49+
tt_content.{$typeDefinition->getTypeName()} >
50+
tt_content.{$typeDefinition->getTypeName()} = WEBCOMPONENT
51+
tt_content.{$typeDefinition->getTypeName()}.componentFolder = $contentBlockExtPath
52+
HEREDOC;
53+
$typoScriptArrayObject->append($typoScript);
54+
}
55+
}
56+
57+
return $typoScriptArrayObject;
58+
}
59+
}

Classes/ViewHelpers/RenderViewHelper.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,13 @@ public static function renderStatic(array $arguments, \Closure $renderChildrenCl
4242
$componentRenderingData->setContentRecord($contentObjectRenderer->data);
4343
try {
4444
$componentRenderingData = self::evaluateComponent($componentRenderingData, $arguments['component'], $contentObjectRenderer);
45+
$event = GeneralUtility::makeInstance(ComponentWillBeRendered::class, $contentObjectRenderer, $componentRenderingData);
46+
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
47+
$eventDispatcher->dispatch($event);
4548
} catch (AssertionFailedException $e) {
4649
return $e->getRenderingPlaceholder();
4750
}
4851

49-
$event = GeneralUtility::makeInstance(ComponentWillBeRendered::class, $contentObjectRenderer, $componentRenderingData);
50-
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
51-
$eventDispatcher->dispatch($event);
52-
53-
if (!$componentRenderingData->isRenderable()) {
54-
return '';
55-
}
56-
5752
$tagName = $componentRenderingData->getTagName();
5853
$content = $componentRenderingData->getTagContent();
5954
$properties = $componentRenderingData->getTagProperties();

Configuration/Services.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ services:
44
autoconfigure: true
55
public: false
66

7+
Sinso\Webcomponents\:
8+
resource: '../Classes/*'
9+
710
Sinso\Webcomponents\ContentObject\WebcomponentContentObject:
811
tags:
912
- name: frontend.contentobject

0 commit comments

Comments
 (0)