- new inline table tx_vitec_product_feature (title, cardtext, FAL icon)
+ `features` field on the product record; ProductShowJsonRenderer
emits product.features {heading, items[{title, text, icon}]} and null
while a product has no cards, so the front end keeps its
applications-HTML fallback
- new vitec:seed-product-features: seeds the cards from the
applications <p><strong> pairs and matches the staged agency icons by
normalised title (camelCase split, stop words, Levenshtein <= 3 typo
tolerance); products with existing cards are never touched
- new vitec:build-why-vitec-bands: "Why VITEC?" intro + icon_card band
on the 7 overview pages with a staged icon set (the contract's
"centered" variant does not exist - documented in ticket 0039-BE);
vision/visionav sub-group sets stay unassigned
- product-check page: sortable title column and an Open Product button
- new migration fix_colombia_quote.php: stray quote element #12037
removed, quote_role set (ticket 0038-FE)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- customer change request (2026-10-26): Resources = Downloads, the
detail band is simplified - capabilities leave the section, Technical
Highlights move left, the resource cards into the right column
(frontend already built on dev.vitec.com)
- TCA: capabilities, datasheets and downloads moved to the Misc tab -
parked, not removed; their JSON keys are unchanged. The planned
automatic newest-datasheet/documentation cards stay deferred by
request (the download route already serves the newest file per
Collateral convention, so the foundation is there)
- new resources_layout select on the Resources tab, English label and
description: Layout 1 = 50-50 (default, highlights left / cards
right), Layout 2 = 33-33-33 (highlights over two columns, cards in
the third); ext_tables.sql column added
- the manual resource card restype vocabulary gains Datasheet and
Brochure (select items plus icon picker seeds, no code change - the
renderer passes restype through as type): an editor can pin a single
datasheet card by linking the download record, which stays current
because the download route always serves the newest file
- ProductShowJsonRenderer: the resources envelope gains sectionLayout
('50-50'|'33-33-33'); verified live - Diamond-H delivers 33-33-33,
untouched products the 50-50 default, layout/items unchanged
- spec bumped to v1.26 (additive revision row, field table 7.15.1),
mirrored; frontend briefing notes the new key
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New record type tx_vitec_domain_model_searchcard ("best bets"):
keyword list, priority, optional referenced record (page, product,
story, solution, market, news) and overridable title/teaser/image/link;
hidden/start/end time allow timed campaigns
- SearchCardService delivers content.search.cards on page 1: max. 3,
priority-ordered, deduplicated by URL, independent of filter, facets
and result count. Keywords match as whole words/phrases, case- and
punctuation-insensitive, including one level of the Solr managed
synonyms in both directions (cached for an hour, fail-soft). Record
cards read the record's indexed Solr document, so a card always
matches the record's own search result
- Index fallback for markets and solutions: when the record carries no
image or text, the document takes the intro image (then the hero
background) and the hero subline, intro headline and intro text from
the content blocks of its detail page - selected by CType, so it works
for layout 21 and layout 5 alike; improves results and cards
- Fix: a search request without q returned an empty body instead of the
documented empty shape (stdClass resolved inside the renderer namespace)
- migrations/check_solution_fields.php: shows live/workspace versions of
solution records
- Spec v1.22 (clause 7.16: cards)
the customer workflow in mind: the customer fills one XLSX per sub-market,
an importer builds the page).
- Frontend layout 21 'Sub-Market Page' in the existing
BackendLayoutDataProvider: six zones, one per Figma section, emitted as
colPos110..colPos160 - the JSON groups plus appearance.layout are the
front-end contract (spec clause 7.17). A first attempt via page-TSconfig
BackendLayouts was the wrong mechanism next to the provider and was
removed again
- New Content Blocks vitec_markethero, vitec_marketintro,
vitec_marketinsights (field bags, serialized unprefixed under data);
the workflow section reuses intro-paragraph + vitec_card flip cards
- Stories gained quote/quote_person/quote_role (Testimonial tab),
maintained once per story, emitted on every story card; usecaselist
gained the layout value 'testimonials'
- New plugin vitec_productfeature: one referenced product as a full
product card, serialisation shared with the product list (cObj made
protected for the subclass)
- vitec_productlist gained hand-picked product selection: exactly the
chosen products in the editor's order, category filter ignored,
visibility filters kept - closes the long-standing single-select gap
- New CLI vitec:generate-submarket-workbooks: one protected customer
XLSX per sub-market (9 main markets, 25 sub-markets from the Market
category tree) - dropdowns filled live from the database, named ranges
as the importer contract, hidden Meta sheet pinning submarket/group/
record uid, soft character-limit warnings, asset URL fields
Verified end to end on Broadcast & Media: layout-21 payload with colPos110
carrying the hero element and its subline. Spec raised to v1.20.
feedback widget served from /api/fe-comment/widget.js, note pad in
localStorage, submissions mailed via /api/fe-comment/submit with
recipients from the extension configuration; classic sites get the
script injected via AssetCollector when the jsonOutput setting is off.
An enabled master switch serves the widget EMPTY when off (no 404, no
console errors) and rejects submissions - the go-live kill switch, no
front-end build change needed
- URL swap finished: /_json prefix middleware, SPA fallback live with a
dedicated root rule (REQUEST_FILENAME of / is a directory) and a
query-string exception for the TYPO3 v14 install tool entry
(?__typo3_install on the site root - the backend Settings/Environment
modules depend on it); /api/ as a whole stays with TYPO3
- MailMessage::send() no longer exists in v14: fe_comment and the form
EmailDelivery (latently broken since the core upgrade) now send through
the Mailer service. Mail transport moved to SMTP against the local exim
so delivery errors surface instead of bouncing into the blackhole -
exim verifies the envelope sender, which therefore must be a routable
existing address (transport config lives in the untracked settings.php)
- Related-product card texts editable on the product record (table rebuilt
with uid/pid, TCA + inline field, one-time migration script); shared
RteCompare for the module badge and the CLI comparison - batch dry-run
now reports 0 writes instead of flagging every richtext field
- Workbook clarification cases executed: VisionIO-SD14, VisionSC-SD14 and
both Diamond OG records imported (40 fields, live-verified); ArtioSign
and ArtioView created hidden (uid 109/110) via
create_artio_records.php - the product table has no sorting column;
WallControl_10 mapping REVERTED and parked: the dry run revealed
aetria-wallcontrol already carries its own agency workbook, which
WallControl_10 would have overwritten. Manifest now 75 product /
24 family / 1 open
Two work packages, one deploy.
Workbook field mapping completed (2026-09-18)
- highlights <- g:Technical Highlight (new spec-row component of the
full agency delivery; composeGroup() is generic, no change needed)
- applications <- g:Why Choose Card (benefit cards as one block)
- seometa <- new composed source x:seometa (Introduction body trimmed
at a word boundary to ~155 chars - the SEO sheet ships no meta
description, a trimmed intro beats an empty tag)
- teaser stays the hero one-liner, settling the varchar(255) question
- fix: "Save mapping" now merges over the stored mapping instead of
overwriting it - an empty dropdown no longer wipes a stored selector
Product model: solutions relation and Resources tab (2026-09-24)
- closes the gaps the full workbook walkthrough exposed (rows the
import deliberately skips because the model had no place for them)
- solutions: MM multi select to tx_vitec_domain_model_solution
(tx_vitec_product_solution_mm, relatedprodukt pattern), emitted by
both product renderers as solution cards in the shared shape
- Resources tab: inline child table tx_vitec_product_resource - per
entry a type (documentation/support), an editable card title/text,
an icon (free text with a value-picker seed set) and one link
(page/url/file/record via the link wizard). Deliberately no Extbase
model: only the UserFunc path reads the rows.
- datasheets: own MM selection of download records below the resource
list (tx_vitec_product_datasheet_mm); emitted into the SAME
resources envelope as ready-made type=datasheet cards (title/teaser/
icon from the record, url = the public /download/<slug> route, uid
fallback - private/hidden downloads 404 as everywhere)
- JSON `resources` (detail payload only, like heroimage): list envelope
{layout, items[]}; a hand-written card whose link is empty or
unresolvable is not emitted; layout switches to carousel above three
cards in total
- solutions hybrid (same day, after analysing all 100 workbooks: their
Solution Cards reference the 8 GROUPS in 98% of cases): new is_group
flag on the solution model, new idempotent CLI
vitec:create-solution-groups (8 group records; title source is
AlignSolutionsCommand::TARGET - now public -, category = the group's
own category, detail_page = the /solutions group page with -N
disambiguation). Group records are invisible to the solution list
and to vitec:align-solutions and only selectable in the product
relation; product solution cards carry isGroup
- spec raised to v1.18 (7.15 member table, 7.13.3 note, revision row;
the footer had been stuck at v1.15 since 1.16/1.17)
Requires on the server:
vendor/bin/typo3 database:updateschema "*.add,*.change"
vendor/bin/typo3 cache:flush
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two work packages, one deploy.
Workbook field mapping completed (2026-09-18)
- highlights <- g:Technical Highlight (new spec-row component of the
full agency delivery; composeGroup() is generic, no change needed)
- applications <- g:Why Choose Card (benefit cards as one block)
- seometa <- new composed source x:seometa (Introduction body trimmed
at a word boundary to ~155 chars - the SEO sheet ships no meta
description, a trimmed intro beats an empty tag)
- teaser stays the hero one-liner, settling the varchar(255) question
- fix: "Save mapping" now merges over the stored mapping instead of
overwriting it - an empty dropdown no longer wipes a stored selector
Product model: solutions relation and Resources tab (2026-09-24)
- closes the gaps the full workbook walkthrough exposed (rows the
import deliberately skips because the model had no place for them)
- solutions: MM multi select to tx_vitec_domain_model_solution
(tx_vitec_product_solution_mm, relatedprodukt pattern), emitted by
both product renderers as solution cards in the shared shape
- Resources tab: inline child table tx_vitec_product_resource - per
entry a type (documentation/support/datasheet), editable card title
and text, an icon (free text with a value-picker seed set) and one
link (page/url/file/record via the link wizard, so the Download tab
works too). Deliberately no Extbase model: only the UserFunc path
reads the rows.
- JSON `resources` (detail payload only, like heroimage): list envelope
{layout, items[]}; a card whose link is empty or unresolvable is not
emitted; layout switches to carousel above three cards
- spec raised to v1.18 (7.15 member table, revision row; the footer had
been stuck at v1.15 since 1.16/1.17)
Requires on the server:
vendor/bin/typo3 database:updateschema "*.add,*.change"
vendor/bin/typo3 cache:flush
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sys_category gains a story selection and a layout (Options tab, alongside
the images field EXT:news puts there), so one editorial decision serves
every product list that renders the category. ProductListJsonRenderer emits
them beside the products in the 7.3 list envelope; each card carries
forCategories - the product categories it came from - so the front end hides
it with the same ?cat=/?subcat= filter it applies to the products. Stories
are collected over the category and its descendants and deduplicated.
Story links follow the existing rule (detail page's parent path plus slug,
which SuccessStoryPathRewrite maps back), taking the page from the element's
FlexForm or the new site setting vitec.storyDetailPid.
Spec raised to 1.17 with clause 7.15.2.1; the header had been left at 1.12
although entries up to 1.15 already existed, so today's two entries were
renumbered to 1.16 and 1.17.
VITEC Import module: Products tab is now a searchable/sortable product
overview. "Edit Product" opens an XLSX upload with per-field source
mapping (component + cell), old/new preview and checkbox apply via
DataHandler; the mapping and manual matches are persisted and preselect
the next workbook. Category workbooks are detected and rejected.
- new: ProductXlsxReader (PhpSpreadsheet), ProductTextImportController,
Products/ProductTexts templates, 4 module routes
- related products: per-pair card text in new side table
tx_vitec_product_related_text (survives MM rewrites), emitted as
`cardtext` in the product JSON; missing MM relations added add-only
- card copy read from Body Copy (D) with fallback to CTA/Card Copy (E) -
the workbooks fill either depending on row type
- product detail page <title> now uses seotitle with title fallback
(provider made singleton and fed from the JSON renderer)
- per-user recent-search badges; last loaded workbook stored per product
(tx_vitec_product_workbook), Edit Product reopens on it
- composer: add phpoffice/phpspreadsheet ^5.9
- diagnostics: migrations/check_workbook.php
- vitec_usecaselist: layout + record selection like marketlist
- new plugin vitec_solutionlist (spec 7.13.3, key "solutions")
- tx_vitec_domain_model_usecase: detail_page -> resolved detailUrl
(also gives story cards in vitec_modelcard a link for the first time)
- "Show Toolbar" checkbox on product/market/solution/usecase lists
- spec bumped to v1.9
BREAKING: vitec_productlist and vitec_usecaselist now emit an object
instead of a bare array. Consumers must read products.products and
usecases.usecases. Also found: productlist had a configurable layout
that was never serialised; it is emitted now, but keeps its own 0-3
vocabulary instead of grid/list/carousel.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
New "VITEC Import" module under Web: one import page per domain model
(v1: Market, Solution, Product - registry-driven, adding a model is one
config entry). Workflow: upload a CSV (delimiter and encoding are
auto-detected, including German-Excel semicolon/Windows-1252), map CSV
columns to DB fields, persist the mapping per model together with the
identity field used for matching (new table tx_vitec_import_mapping,
no TCA - pure tool configuration), review a unified list of CSV rows
matched against the DB records (new / update with differing fields /
unchanged / db-only), then apply the checked rows through DataHandler.
Each importable row carries an editable JSON payload textarea - what
is written is the textarea content, not the raw CSV, so editors can
fix values right in the review step. The parsed CSV travels through
the form as a hidden JSON field: no session state, no temp files.
Importable fields are derived from TCA at runtime (scalar types only;
files, categories and other relations are excluded - a flat CSV
cannot carry them). Payloads are whitelisted against that field list
on apply; new records require a storage pid (prefilled from existing
records). BE user permissions apply via DataHandler.
The module ships its own CSS (backend-import.css, loaded only by
this module) using the frontend button palette from _vitec.scss:
orange #f47937 for primary actions, navy #26358c for secondary
actions and structure. The stray <h2>Hi</h2> debug leftover in the
shared backend layout is removed (also affects the OG Image module).
A fourth tab "SEO Research" handles the recurring keyword-research
CSV. It is deliberately not an import mask - the file carries research
only (no meta title/description yet). Each upload is persisted as a
delivery (tx_vitec_seo_research, never deleted) and evaluated: diff
against the previous delivery keyed by URL, a structure check of the
CSV tree against TYPO3 (pages by slug path; market/solution/product
rows against the domain tables, matched by slug then normalized
title), and the three work lists from the SEO flags (quick wins by
GSC impressions, shared terms grouped by keyword, already ranking).
CsvReader now deduplicates repeated header names, which that CSV has.
New CLI command vitec:create-markets: creates the market records the
structure check reports as missing, sourced from the stored delivery
and matched through the same SeoResearchService - what the module
lists is what the command creates. Each market gets a sys_category of
the same title, found anywhere under the auto-detected market category
root or created; sub-market categories are created under the parent
market's category, so the category tree carries the hierarchy the
flat market model cannot. Idempotent, dry-run first.
New CLI commands vitec:create-markets and vitec:market-dummy-image.
create-markets creates the market records the structure check reports
as missing (root detection three-staged: option, auto-detect, find or
create a "Markets" category). market-dummy-image assigns a shared
placeholder (white logo on brand navy, fileadmin/placeholders/) to
every market without an image - one sys_file for all, replacing the
file restyles every placeholder at once. Both idempotent.
Deliberately out of v1: import log with three-way compare (protection
against overwriting manual edits), images/relations, multiple saved
mappings per model.
New "VITEC Import" module under Web: one import page per domain model
(v1: Market, Solution, Product - registry-driven, adding a model is one
config entry). Workflow: upload a CSV (delimiter and encoding are
auto-detected, including German-Excel semicolon/Windows-1252), map CSV
columns to DB fields, persist the mapping per model together with the
identity field used for matching (new table tx_vitec_import_mapping,
no TCA - pure tool configuration), review a unified list of CSV rows
matched against the DB records (new / update with differing fields /
unchanged / db-only), then apply the checked rows through DataHandler.
Each importable row carries an editable JSON payload textarea - what
is written is the textarea content, not the raw CSV, so editors can
fix values right in the review step. The parsed CSV travels through
the form as a hidden JSON field: no session state, no temp files.
Importable fields are derived from TCA at runtime (scalar types only;
files, categories and other relations are excluded - a flat CSV
cannot carry them). Payloads are whitelisted against that field list
on apply; new records require a storage pid (prefilled from existing
records). BE user permissions apply via DataHandler.
Deliberately out of v1: import log with three-way compare (protection
against overwriting manual edits), images/relations, multiple saved
mappings per model.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>