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

@@ -8,91 +8,129 @@ config {
}
}
@import 'EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript'
# =============================================================================
# Headless list-plugin JSON renderers
# Headless list-plugin JSON renderers (TYPO3 v14: each plugin is its own CType)
#
# `tt_content.list.20.<list_type>` renders the element body for that subtype
# (only ever invoked for the matching list_type — safe as-is).
#
# `tt_content.list.fields.content.fields.<key>` is GLOBAL: without scoping it
# would inject <key> into EVERY list plugin's content. Each such field is
# therefore guarded with:
# stdWrap.if { equals.field = list_type ... } -> empty for other plugins
# ifEmptyUnsetKey = 1 -> empty key removed entirely
# (headless JsonContentObject honours ifEmptyUnsetKey: '' / false => unset).
# For every VITEC plugin the renderer's USER output is exposed as a sub-field
# of the standard content-element envelope (`content.<key>`), inheriting all
# default fields (id, type, colPos, categories, appearance, …) from
# lib.contentElement provided by friendsoftypo3/headless.
# =============================================================================
# --- vitec_productlist --------------------------------------------------------
tt_content.list.20.vitec_productlist = USER
tt_content.list.20.vitec_productlist {
userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
}
tt_content.list.fields.content.fields.products = USER
tt_content.list.fields.content.fields.products {
userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
stdWrap.if {
value = vitec_productlist
equals.field = list_type
tt_content {
vitec_productlist < lib.contentElement
vitec_productlist {
fields {
content {
fields {
products = USER
products.userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
}
}
}
}
ifEmptyUnsetKey = 1
}
# --- vitec_productshow --------------------------------------------------------
tt_content.list.20.vitec_productshow = USER
tt_content.list.20.vitec_productshow {
userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
}
tt_content.list.fields.content.fields.product = USER
tt_content.list.fields.content.fields.product {
userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
stdWrap.if {
value = vitec_productshow
equals.field = list_type
vitec_productshow < lib.contentElement
vitec_productshow {
fields {
content {
fields {
product = USER
product.userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
}
}
}
}
ifEmptyUnsetKey = 1
}
# --- vitec_usecaselist --------------------------------------------------------
tt_content.list.20.vitec_usecaselist = USER
tt_content.list.20.vitec_usecaselist {
userFunc = Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer->render
}
tt_content.list.fields.content.fields.usecases = USER
tt_content.list.fields.content.fields.usecases {
userFunc = Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer->render
stdWrap.if {
value = vitec_usecaselist
equals.field = list_type
vitec_usecaselist < lib.contentElement
vitec_usecaselist {
fields {
content {
fields {
usecases = USER
usecases.userFunc = Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer->render
}
}
}
}
ifEmptyUnsetKey = 1
}
# --- vitec_usecaseshow --------------------------------------------------------
tt_content.list.20.vitec_usecaseshow = USER
tt_content.list.20.vitec_usecaseshow {
userFunc = Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer->render
}
tt_content.list.fields.content.fields.usecase = USER
tt_content.list.fields.content.fields.usecase {
userFunc = Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer->render
stdWrap.if {
value = vitec_usecaseshow
equals.field = list_type
vitec_usecaseshow < lib.contentElement
vitec_usecaseshow {
fields {
content {
fields {
usecase = USER
usecase.userFunc = Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer->render
}
}
}
}
vitec_marketshow < lib.contentElement
vitec_marketshow {
fields {
content {
fields {
market = USER
market.userFunc = Evomedien\Vitec\UserFunc\MarketShowJsonRenderer->render
}
}
}
}
vitec_solutionshow < lib.contentElement
vitec_solutionshow {
fields {
content {
fields {
solution = USER
solution.userFunc = Evomedien\Vitec\UserFunc\SolutionShowJsonRenderer->render
}
}
}
}
vitec_downloadcard < lib.contentElement
vitec_downloadcard {
fields {
content {
fields {
downloadcard = USER
downloadcard.userFunc = Evomedien\Vitec\UserFunc\DownloadcardJsonRenderer->render
}
}
}
}
vitec_downloadcardcollection < lib.contentElement
vitec_downloadcardcollection {
fields {
content {
fields {
downloadcardcollection = USER
downloadcardcollection.userFunc = Evomedien\Vitec\UserFunc\DownloadcardcollectionJsonRenderer->render
}
}
}
}
vitec_datasheets < lib.contentElement
vitec_datasheets {
fields {
content {
fields {
datasheets = USER
datasheets.userFunc = Evomedien\Vitec\UserFunc\DatasheetsJsonRenderer->render
}
}
}
}
ifEmptyUnsetKey = 1
}
# Include container (layout) rendering definitions
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript'
# Ensure headless's content element JSON definitions take precedence over
# fluid_styled_content's HTML definitions. This is loaded at the end of the
# Vitecset to guarantee winning load order.
@import 'EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript'
# Include menu (navigation) JSON definitions
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript'