VITEC headless v14: 5-bug fix unifies plugin renderers + cleanup

Resolves a cascade of TYPO3 v14 breaking changes that left every VITEC
plugin's JSON output silently empty:

1. `list_type` column dropped — all renderer page-discovery queries now
   filter by `CType = 'vitec_X'`.
2. `#[AsAllowedCallable]` attribute added to every public render() —
   without it v14 throws AllowedCallableException, which headless
   silently filters via its "Oops, an error occurred!" guard.
3. `$GLOBALS['TSFE']->id` is null inside JSON cObj context — page id
   now read from the `frontend.page.information` request attribute
   (TSFE fallback kept for legacy entry points).
4. page.tsconfig wizard items migrated to `CType = vitec_X` directly
   (legacy `CType=list, list_type=...` no longer exists in v14).
5. ContainerChildrenProcessor + ContentElementResolver dispatch the
   PLUGIN_RENDERERS map by CType (primary) with list_type fallback.

Beyond the bug fix this commit also contains:
- Datasheets renderer rewritten to "products with newest datasheet"
  semantics (filtered via Download.hideondatasheets).
- New vitec/card content block — multi-purpose card with backend
  preview and layout/background/aspect/alignment/border/shadow options.
- New vitec_container CType (b13 single-column container, FlexForm
  cssClass propagated into the JSON envelope).
- ContentElementResolver service for contentelement/contentelementcta
  link resolution; ContainerChildrenProcessor for nested plugin
  resolution inside b13 containers.
- Vitecset setup.typoscript: ContentElement import moved to top so
  lib.contentElement is defined before VITEC's tt_content blocks.
- Cleanup: 98 .bak.<timestamp> debugging backups removed; *.bak.* and
  /public/_assets_install/ added to .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
o-rasche
2026-06-15 14:29:17 +02:00
parent 64ea55d29d
commit 3aaf8fdad5
98 changed files with 5650 additions and 10154 deletions

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Evomedien\Vitec\UserFunc;
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
use Doctrine\DBAL\ParameterType;
use Evomedien\Vitec\Domain\Repository\ProductRepository;
use Psr\Http\Message\ServerRequestInterface;
@@ -25,9 +27,30 @@ use TYPO3\CMS\Extbase\Service\ImageService;
*/
class ProductListJsonRenderer
{
#[AsAllowedCallable]
public function render(string $content, array $conf): string
{
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
// 1) cObj data path
$row = is_array($this->cObj->data ?? null) ? $this->cObj->data : null;
if ($row && (string)($row['CType'] ?? '') === 'vitec_productlist') {
return $this->renderForRecord($row);
}
// 2) Page-id via v14 request attribute (TSFE->id is often null in JSON cObj context)
$pageId = 0;
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
if ($request !== null) {
$pageInfo = $request->getAttribute('frontend.page.information');
if ($pageInfo !== null) {
$pageId = (int)$pageInfo->getId();
}
}
if ($pageId <= 0) {
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
}
if ($pageId <= 0) {
return '';
}
$queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class)
->getQueryBuilderForTable('tt_content');
@@ -37,7 +60,7 @@ class ProductListJsonRenderer
->from('tt_content')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
$queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)),
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)),
$queryBuilder->expr()->eq('deleted', 0),
$queryBuilder->expr()->eq('hidden', 0)
)
@@ -45,13 +68,13 @@ class ProductListJsonRenderer
->fetchAllAssociative();
if (empty($contentElements)) {
// Not a product-list page: emit nothing so headless removes the key.
return '';
}
return $this->renderForRecord($contentElements[0]);
}
/**
* Render exactly the given tt_content row (the product-list plugin element).
* Exception-safe: returns '' on any failure.