Kev's keyword CSV and TYPO3 hold the same solutions under slightly
different names, so the structure check filed them as missing/extra.
Matching is now slug -> normalized title -> record alias, using the
per-model alias store the import tabs got on 2026-08-18
(tx_vitec_import_mapping.record_aliases, key = lowercased CSV name).
One link therefore serves the import union list, the structure check
and vitec:create-markets (same service - linked rows are no longer
proposed as new records).
- every "Missing in TYPO3" row carries a select of all records the
direct match did not claim; "Save record links" persists per model
- alias-matched rows show under "Both" with a "linked" badge and the
same select; option 0 removes the link
- SeoResearchService::recordsCheck() additionally returns `linkable`
(the select options) and flags alias matches with via=link
- new backend POST route seo_aliases
Success stories: --markets-only repair mode
- /success-stories emitted empty `markets` for all 48 stories - a data
problem (code and TCA untouched since 17.08.); read-only diagnosis
script migrations/check_usecase_markets.php added
- vitec:import-success-stories --markets-only re-derives each story's
industries from the export exactly like the full import and rewrites
ONLY the markets MM relation of the existing record via DataHandler;
nothing else is touched, no usecase created or deleted, unresolved
stories keep their current relations
- decision: industries are translated onto the nine main markets of
the current taxonomy (INDUSTRY_TO_MARKET) instead of recreating the
seven deleted industry markets; the repair mode never creates
market records (root cause: those industry markets were deleted
after the 07.08. taxonomy import, orphaning all story relations)
- ensureMarkets() gained a $create flag for reuse by the full import
Story detail pages: real page title
- every story page answered with the generic page title "Story"; same
defect and same fix as the product pages on 21.08.: new
UsecasePageTitleProvider (singleton), registered as vitecUsecase in
config.pageTitleProviders, fed from UsecaseShowJsonRenderer with
seo_title falling back to title
Requires on the server:
vendor/bin/typo3 cache:flush
Closes two annex items that turned out to be the same defect seen from two
sides: markets could link to a detail page, solutions could not, and the code
that turns a page uid into a URL existed four times over.
detail_page for Solution (B-11)
- ext_tables.sql, TCA (group on pages, maxitems 1, in showitem behind slug)
and Domain\Model\Solution mirror the Market field exactly
- SolutionShowJsonRenderer emits detailUrl
- ModelcardJsonRenderer serialized no detailUrl at all: its shared
market|solution branch never carried the field. Market modelcards therefore
gain a link here too, not just solutions.
Service\LinkResolver (B-12)
- one implementation replaces four private copies (Market list/show, Usecase
list/show). The copies had drifted: Market returned null on failure, Usecase
an empty string. The contract is now explicit -- null means "no link".
- Usecase keeps a thin resolvePageUrl() wrapper appending ?? '', because it
concatenates paths and a null would tear the strings apart.
- LocationsJsonRenderer and NewsJsonRenderer stay out on purpose: they resolve
a full parameter construct resp. slug paths plus canonical, which is not
page-uid-to-URL and does not fit the same contract.
Requires on the server, the column exists in code only:
vendor/bin/typo3 database:updateschema "*.add,*.change"
vendor/bin/typo3 cache:flush
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.
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>
- New Classes/Service/ContentElementResolver.php: parses TYPO3 typolink
(t3://record?identifier=tt_content&uid=N, t3://page?uid=P#N, plain numeric)
to a normalised tt_content envelope ({id,type,colPos,sorting,appearance,
data}), recursively resolving any nested VITEC list-plugin children.
- ProductListJsonRenderer / ProductShowJsonRenderer: contentelement and
contentelementcta now emit the resolved object instead of the raw
typolink string (breaking change at those two keys).
- Product model: new field videofile (FAL, single video upload in tab
Images and Videos, mp4/webm/ogv/mov/m4v); new bool field showdatapath
(toggle at end of General tab, controls the "Datapath is now Vitec"
graphic in the frontend). ext_tables.sql, TCA, model property +
getter/setter, and both renderers updated accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>