- Extract the complete import logic (label-scan reading, page
find-or-create, slot filling, content-idempotent image attach) from
ImportSubmarketWorkbooksCommand into the shared service
SubmarketWorkbookImporter; the CLI command and the new controller are
thin shells around importFile(), so both entry points cannot drift.
CLI regression verified: identical dry-run report for the reference
workbook (12 elements, page 443)
- New "Sub-Markets" tab in the VITEC Import module
(SubmarketImportController + Import/Submarkets.html): upload multiple
filled workbooks into the SAME inbox the CLI uses
(migrations/submarkets_xlsx_in/), per-file checkboxes, dry-run and
apply buttons, result cards with the familiar report and a direct
link into the page module; backend dry-run verified against the CLI
- Submitted filenames are whitelisted against the actual directory
listing (no path traversal); uploads restricted to sanitized .xlsx
basenames
- Routes submarkets/submarket_upload/submarket_process in Modules.php,
Sub-Markets tab link in the sibling module views, ok/danger badge
colors in backend-import.css
- Docs section 5y (incl. operations note: apply in small batches in the
module, mass runs via CLI - image downloads run inside the web
request)
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/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>
- vitec:migrate-product-structure: exact V2 titles, Milestone typo fix (incl.
slug), QTX100 stray VSN category removed, Aligo record moved to its own
category with slug /product/aligo, new Aligo Workstation record takes over
/product/aligo-workstation
- product families (sitemap column K) are ordinary content pages now:
vitec:remove-family-records deletes the interim landing records again,
reset_subproduct_flags.php clears the misused subproduct flag (the list
renderer has always excluded subproduct=1)
- vitec:import-product-workbook: non-interactive counterpart of the module
import (same reader, mapping and DataHandler path); Arqa imported, Aligo
re-applied with the corrected capabilities markup
- ProductXlsxReader: new :copy cell selector (Body Copy with CTA fallback)
absorbs the agency's column drift; default teaser mapping uses it
- ProductListJsonRenderer: selected categories now match their whole subtree,
results ordered by category tree position, sheet order within a category
- read-only diagnostics: check_product_structure.php, check_productlist_ces.php
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
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
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.
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).
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>
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>