54 Commits
Author SHA1 Message Date
orascheandClaude Fable 5.1 20bcbeb731 Page content check for markets and solutions (vitec_pagecheck)
Sibling of the product check (decision Harry 2026-11-13): new content element vitec_pagecheck with a FlexForm scope (markets | solutions) and PageCheckService/PageCheckJsonRenderer. Overview = one row per page of the tree (11 markets + 24 sub-markets, 8 solution groups + 39 sub-solutions) with 18 checks taken from the page elements (hero image and subline, lead, intro text/image/CTA, cards with visuals, testimonials, products, insights text/cover/CTA, logos, CTA banner), the page properties (meta description, OG image, page media) and the market/solution record behind the page; columns a page type does not have come as ok null. ?check=<pageUid> delivers the page detail (elements in page order with texts, links, images, FlexForm settings; SEO, media, record, children). Always USER_INT, check is cHash-excluded already. New command vitec:pagecheck-pages created /markets/market-check (608) and /solutions/solution-check (609) from the product-check page properties; both live on DEV, rendering is front end work.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-13 11:44:41 +01:00
orascheandClaude Fable 5.1 47c8e2902f Market pages: elements for the Figma structure (0124-FE)
New command vitec:markets-figma-elements (dry-run by default, --apply writes) for the 11 main market pages: numbered placeholder image placeholders/pages/<uid>-02.png and learnmore_target /contact on vitec_marketintro, the hidden Trusted-by and Case-studies shells of the 2026-10-23 page import unhidden (logos with the home page settings, case studies as testimonials with the empty selection = all stories, like the sub-market pages), the lead paragraph in the hero slot hidden (decision aehm 12.11., not deleted). Sub-market pages: vitec_marketinsights CTA /success-stories -> /insights/news with the label Explore our insights. Applied on DEV 2026-11-12: 11 images, 11 targets, 11 logo bands, 11 sliders, 11 hidden leads, 24 CTAs, 0 warnings; protocol in migrations/markets_figma_apply_20261112.txt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-12 19:14:05 +01:00
orascheandClaude Opus 5.5 6a00926b89 vitec:ssr-health --test-mail: send a sample alert through the real alert path
Deliverability check (e.g. mail-tester.com) with the same sender, mailer and plain-text format as a real alert. Optional address; without one the configured alert recipients get it. No health sample is taken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 15:39:53 +01:00
orascheandClaude Opus 5.5 e14f94ba63 SSR auto-purge: purge only the pages that show a saved record, megamenu purges everything
SsrPurgeTargetResolver finds the paths of a saved VITEC or news record: its detail URL, the pages that select it (sys_refindex), product lists by category or all products, list plugins of its type and the records embedding it one level deep. URL, visibility and timing changes, the newest news, events, unknown tables and more than 150 paths still purge everything. Megamenu records and the megamenu element now purge everything (before: only its storage page). New read-only command vitec:ssr-purge-targets shows what a save would purge. Reference index rebuilt (117 updates).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 12:16:44 +01:00
orascheandClaude Opus 5.5 b36dceb486 Scheduler: VITEC tasks instead of separate crontab lines
New command vitec:scheduler-setup creates the scheduler group VITEC with the tasks vitec:ssr-health every 5 minutes and vitec:client-error-cleanup daily at 04:23, safe to run again; --disabled creates them switched off, --enable switches them on after the old crontab lines are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 11:21:53 +01:00
orascheandClaude Opus 5.5 90cb2faaf9 SSR alert mail recipients maintainable in the site settings
New site setting vitec.ssr.alertEmails (Site Management, Settings, VITEC; one address per entry). vitec:ssr-health reads the recipients from there, invalid entries are skipped and logged, SSR_ALERT_EMAIL in the env file stays as fallback. The cockpit shows the recipients in the live band. Pre-filled with the address that was in the env file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 11:13:37 +01:00
orascheandClaude Fable 5.1 5824981e2b Overview-Import: Why-VITEC-Karten als icon_card (0106-FE)
Der erste Lauf (5bm) legte die neuen Why-VITEC-Baender auf 9 Uebersichts-
seiten als text_only an - das Frontend baut das Icon-Band nur aus icon_card.
Importer legt neue Band-Karten jetzt als icon_card (ohne Icon) an und stellt
vorhandene Band-Karten beim Re-Run um; 36 Karten repariert, Seiten gepurgt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-11 10:11:32 +01:00
orascheandClaude Fable 5.1 f4f18e913a Produkt-Uebersichtsseiten: Agentur-Texte aus den "Enriched"-Workbooks importiert
- neues CLI vitec:import-product-overview-workbooks (Dropbox-Lieferung 09.11.,
  15 Workbooks): Hero-Subline/CTA, Intro + Use Case, Why-VITEC-Band (Text-
  karten bzw. Text auf passende Icon-Karten), Range-Intro, Solution-Karten
  mit Link auf die Gruppenseite, CTA-Banner, SEO-Meta; Produktkarten bleiben
  (Record-Teaser), Vergleichstabelle nur mit --comparison
- vitec:seed-product-features --attach-icons: gestagte Icons an vorhandene
  Karten ohne Icon haengen (PRISM 5/5, SFF PRISM 4/5 angehaengt)
- Diagnose-Skripte check_overview_pages_20261110 / check_banners_20261110
- .gitignore: migrations/product_overviews_xlsx_in, product_images_pending

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-10 19:37:15 +01:00
orascheandClaude Fable 5.1 5121eaa000 Client-Error-Endpoint POST /api/vitec/client-error (0072-FE / 0077-BE)
Neue PSR-15-Middleware ClientErrorMiddleware: nimmt Browser-Fehler des
React-Frontends als JSON {url, message, stack, release, userAgent}
entgegen und antwortet 204 ohne Body. Schutz: 30 Meldungen pro Minute
und IP (Sliding Window ueber TYPO3-RateLimiter, 429 + Retry-After),
8-KB-Body-Limit (413, Content-Length und tatsaechliche Bytes), kein
JSON-Objekt oder leere message = 400, GET = 405. Keine Session, keine
Cookies; der /_json-Praefix funktioniert ebenfalls.

Speicherung in der neuen Tabelle tx_vitec_clienterrorlog mit
anonymisierter IP (IPv4 letztes Oktett 0, IPv6 /48). Sichtbar als
Tabelle "Client errors (last 24 h)" im Modul VITEC SSR (mit Stack als
Aufklapper) und als Zeile "Client errors reported (24 h / 7 d)" in der
Dashboard-Kachel VITEC: SSR statistics.

CLI vitec:client-error-cleanup [--days=30] loescht Eintraege aelter als
30 Tage (taeglich per Cron).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-10 17:25:23 +01:00
orascheandClaude Fable 5 be163a88c2 SSR health alerts: mail texts in English
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-10 11:00:56 +01:00
orascheandClaude Fable 5.2 8f8ae039d4 Real JSON null for empty product feature; SSR warm-cache button and CLI
Ticket 0068-FE: ProductFeatureJsonRenderer returned json_encode(null), which headless passes on as the STRING null (it only decodes object/array-looking strings) and crashed the front end. It now returns an empty string and the TypoScript field sets ifEmptyReturnNull = 1, giving a real JSON null. No other renderer used the pattern. Ticket 0066-FE: SsrPurgeService::warm() posts to the node /__warm endpoint (derived from SSR_PURGE_ENDPOINT, same secret), exposed as a Warm cache button with confirmation in the VITEC SSR module, a Cache warming progress row fed by /__health, and the CLI vitec:ssr-warm [paths]. Warm runs are deliberately not written to the purge log. Verified: two-path run 2/2 done, affected page delivers productfeature null.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 10:59:24 +01:00
orascheandClaude Fable 5 cb53ff67ee SSR dashboard: new errors-and-hit-rate tile fed by /__stats samples in vitec:ssr-health (stat_* columns in healthlog)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-09 22:51:13 +01:00
orascheandClaude Fable 5 bc2e031b0c SSR health: alert mails on ok/down transitions + auto-failover to SPA after N consecutive misses (SSR_ALERT_EMAIL / SSR_FAILOVER_AFTER in var/vitec-ssr-purge.env)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-09 22:18:06 +01:00
orascheandClaude Fable 5 63f950f9db SSR control center: auto-purge webhook, dashboard tiles, backend module
Package 1: SsrPurgeService posts to the node /__purge (endpoint+secret in var/vitec-ssr-purge.env, outside git) and logs every attempt to tx_vitec_ssrpurgelog. DataHandler hooks queue per request and flush once: pages with nav-relevant field changes and all tx_news_/tx_vitec_ records purge everything, other page/content edits purge the hosting page path. Gated by the routing flag and the new Site Set switch vitec.ssr.autoPurge. Flipping the SSR switch ON purges everything once. CLI: vitec:ssr-purge, vitec:ssr-health (samples /__health into tx_vitec_ssrhealthlog, 14 days kept, scheduler-ready). Package 2: dashboard widgets vitecSsrStatus (live health, release, switch, last purge) and vitecSsrStats (purge counts, failures, uptime, response time). Package 3: backend module web_vitecssr with manual purge (paths or everything) and both protocols. Tested end to end against the running node (purge single/all 200, health sample logged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-09 20:46:16 +01:00
orascheandClaude Fable 5 2c0da281bd SSR switch: site setting vitec.ssr.enabled gates .htaccess proxy to SSR node
- Site set Vitecset: new bool setting vitec.ssr.enabled (default off,
  backend switch under Site Management > Settings > VITEC Settings)
- SsrFlagService derives var/vitec-ssr-on.flag from the setting;
  SyncSsrFlag middleware (frontend + backend) re-syncs per request
  because TYPO3 v14 fires no event on site settings save;
  vitec:ssr-flag CLI command for status/--sync
- public/.htaccess: flag-gated block proxies page requests [P] to
  https://vitec-ssr.evomedien.de (mod_proxy incl. https target verified
  on this host 2026-11-09), same exclusions as the SPA fallback block;
  completely inert while the flag file is absent
- Emergency off without TYPO3: delete var/vitec-ssr-on.flag
- Tested end to end: flag on -> pages proxied to VPS Caddy (502 while
  the SSR node is not deployed yet), /_json untouched; flag off ->
  SPA shell exactly as before

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-09 13:35:27 +01:00
orascheandClaude Opus 5.5 bd7a145afa Products: categories and sub-categories become clickable pages
The 2026-11-02 menu build created the 3 categories and 9 sub-categories
as sysfolders (labels); every level must be a clickable page.

- build-product-overviews: six sub-categories adopt the 2026-10 category
  pages (Joe's workbooks; content, slug and SEO kept, sitemap name as
  nav_title), the remaining folders become pages with generated content,
  Capture & Graphics gets the main column of Graphics & Multi-Display and
  Video Capture copied in. Pages move/convert by direct UPDATE (the
  redirects slug hook would rewrite the flat child slugs); three 2026-10
  pages leave the menus but stay reachable. Warns about a sys_redirect
  on a target slug.
- ProvideProductCatalog: categories[].link and groups[].link (additive),
  menu title = nav_title when set, only filled groups/categories count.
  The menu search reads both levels as pages too.
- build-why-vitec-bands: band at the top of the main column; on pages
  with an existing "Why VITEC?" section the context's icons go onto the
  cards whose title matches the icon theme (16 icons on 4 pages).
- No browser cache headers on the page JSON: headless enables
  sendCacheHeaders and TYPO3 14 caches pages for a year by default, so
  every /_json/ response carried max-age=~31536000.
- Spec v1.32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 12:05:04 +01:00
orascheandClaude Fable 5 6ff9c3e7a8 seed-product-features: two more icon-matching tiers
- token-containment pass: theme words inside the card title
  (ManagementFoundation -> "Aetria management foundation")
- last-pair rule: a complete set (#icons == #cards) with exactly one
  icon and one card left over pairs them (FlexibleConnection,
  EndToEndEncryption, HighPerformance, 4KSourcetoDisplays, AVoverIP)
- genuine mismatches (aligo-workstation, EZ-TV Platform, AvediaPlatform
  Artio icons) still stay unmatched for the editors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-04 20:03:50 +01:00
orascheandClaude Fable 5 46b12d4a58 product.features: Why-choose cards with icons (contract 0036-FE)
- 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>
2026-11-04 19:55:01 +01:00
orascheandClaude Fable 5 8e173b1a85 Hero placeholder fieldname: vitec_hero_bgimage (content block prefix)
The vitec/herosection content block has prefixFields - the real column
and sys_file_reference fieldname is vitec_hero_bgimage; the JSON merely
strips the prefix. The first apply wrote the 20 references with the bare
name, so they existed but never rendered (no herosection anywhere had an
image, so there was no working example to catch it against).

- BuildProductOverviews: attach/backfill/skip-check now use
  vitec_hero_bgimage
- new migration fix_overview_hero_refs.php: renames the existing 20
  references in place (no re-attach) and sets the counter column

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-03 18:42:06 +01:00
orascheandClaude Fable 5 77661bf0b6 Datapath stories, grid repairs, menu v2 pure, hero placeholders, fec test
Batch of 2026-10-28/29 plus 2026-11-03:

- NEW vitec:import-datapath-stories: 22 story packages from the agency
  dump (docx + Links/ assets) as usecase records with content, quotes,
  images, customer logos, download cards; --repair-quotes normalizes
  attribution lines; asset backfill for existing stories
- /products grid incident repaired (fix_products_grid, fix_graphics_grid,
  check_grid_page): containers unhidden, 46 duplicate cards soft-deleted,
  section order restored; BuildProductPortfolio and
  ImportProductCategoryPages now GUARD against re-runs on pages that
  already carry vitec_cols_% containers
- &apos; double-encoding fixed: render-pipeline importers write literal
  apostrophes (ENT_NOQUOTES), fix_double_entities literalizes existing rows
- ticket quick wins (ticket_quickwins_20261028), broadcast market page
  cleanup, AttachCardImages market-card handling, SubmarketWorkbook
  META REPAIR, story content/video fixes (fix_story_*)
- ProvideProductCatalog: transitional v1 fields removed (categories
  uid/link, legacyGroupFields, item products teaser) - pure v2 shape
  (ticket 0024-FE); search renderer keeps its teaser
- BuildProductOverviews: heroes get the numbered placeholder background
  (placeholders/pages/<uid>-01.png, wide grey fallback) - backfill on
  existing pages with empty hero_bgimage, attached at creation for new
  pages; existing references always win (ticket 0026-FE); 40 placeholder
  PNGs shipped to fileadmin (not tracked)
- AttachPageMedia: --depth=3 with sysfolders as pass-through parents for
  the overview pages' -08 menu tiles
- NEW migrations/fec_isolation_test.sh: two-user live test proving
  fe_comment ticket isolation (backend verified tight, cross-user
  sighting traced to the FE page-JSON cache, ticket 0027-BE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-03 18:29:56 +01:00
orascheandClaude Opus 5.5 9211c8a395 Megamenu: rail display for page trees, cache tags, editorial promo boxes
- Page-tree entries get a "Page tree display" field (columns | rail |
  rail-compact), emitted as display + cardSize: Markets and Solutions
  show their main pages as a list and the sub-pages as image cards on
  hover. Page-tree items carry image (page media) and teaser (abstract)
  from stage 2. Additive; front ends ignoring display render columns.
- vitec:attach-page-media gains --depth=2 and falls back to the page's
  hero background before the grey placeholder; the 39 sub-solution
  pages got their hero image as page media.
- MegamenuJsonRenderer tags every page carrying the menu with the menu
  tables and tt_content_<uid>, so saving menu records refreshes all
  pages instead of waiting for a full flush.
- Products catalog: drop catalog.news; both promo boxes ("Product
  highlight", "Product news") are the entry's editorial teaser cards.
  ProvideInsightsSpotlight back to its previous state.
- Spec v1.29 and v1.30 (header version corrected), README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:35:23 +01:00
orascheandClaude Opus 5.5 9fb7f0ca77 Products: 20 overview pages and megamenu catalog v2
Rebuild the product section after the agency sitemap and menu prototype
of 2026-11-02:

- vitec:build-product-overviews builds sysfolder labels (3 categories,
  9 groups) below /products with 20 overview pages /products/<slug>
  (layout 9: hero, intro per group, product cards, contact CTA) and two
  menu shortcuts. Dry-run by default, idempotent, explicit sorting.
- ProvideProductCatalog reads that page tree and emits catalog version 2:
  categories[].groups[].items[] of type page (productCount, products,
  image) or shortcut (target indices), plus viewAll, news and search.
- Transitional v1 fields (groups[].products/link/uid, categories[].uid/
  link, product teaser) keep the deployed front end alive; it read the
  v1 shape and rendered a white page without them. Remove once the
  front end reads items.
- ProvideInsightsSpotlight::latestNews() is public with a count.
- Product type-ahead (type 7385) adds matching overview pages and a
  per-product overview with its menu path.
- Spec v1.28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:06:44 +01:00
orascheandClaude Fable 5 d87e64f73b Success stories: downloads and product sections become real plugins
- comparison report migrations/check_product_downloads.php (kept,
  read-only): download records vs. the delivered product workbooks -
  34 of 74 workbook products have a datasheet, the entire Datapath
  line has none in the pool (import came from vitec.com; queued for
  Joe), ~35 orphan datasheets belong to products without records;
  a vitec:link-product-downloads re-run added seven clean links
  (Artio x2, Diamond-H, the three TOUGH records, X-View)
- RTE resolution fixed at the root: ContentElementResolver and
  ContainerChildrenProcessor passed bodytext RAW into the payload, so
  t3:// links leaked unresolved; both now run RTE columns (bodytext,
  vitec_quote) through RteResolver::html()
- the imported "Download Success Story" RTE buttons carried the wrong
  link identifier (tx_vitec_download -> download, one-time migration
  fix_download_record_links.php) AND old-site uids - missing here or,
  worse, accidentally pointing at foreign PDFs. New
  vitec:convert-story-download-buttons replaces them with real
  vitec_downloadcard plugins: the download is resolved by STORY title
  (manual map for ISR / "Okada Manilla", exact-title fallback for
  suffixless PDFs like "Lord's Cricket Ground"); 29 converted, 4
  stories without a delivered PDF have their button hidden. New
  FlexForm layout "Success Story Download" (story_download) applied
  everywhere; the FE renders it as the previous centered button
- the ADIB "Featured Products" text section (the only one site-wide,
  verified by scan_story_headers.php) is now ONE vitec_productlist
  with hand-picked products via old-name aliases and the new FlexForm
  layout "Success Story Products" (story_products); the four text
  elements are deleted. vitec:convert-story-product-sections stays
  re-runnable for future imports
- the productlist envelope additionally carries "columns" (= number
  of products, capped at 4) so the story grid renders as many columns
  as it has products - additive key, available to every product list;
  and hand-picked lists now ship successStories as an empty array (the
  empty category selection used to aggregate stories over the WHOLE
  tree, leaking story cards into the section)
- the story downloadcards carry header "Downloads" (set on convert
  and backfilled): the front end builds the story anchor nav from
  element headers, so the section now appears there
- diagnosis scripts kept: check_download_record, check_story_elements,
  scan_story_headers; verified live on the ADIB payload (structured
  download object, productlist [5,18,6], texts 10 -> 5)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-28 14:10:59 +01:00
orascheandClaude Fable 5 b0690c4cdc Cards visible in the FE; /products cards wrapped into 33/33/33 grids
- the freshly attached card images did not show: text_only is the
  explicit no-image variant, so vitec:attach-card-images now flips a
  card to "vertical" whenever it carries (or receives) an image; and
  the new placeholder PNGs sat in the sshfs 770 trap - fix-asset-perms
  covers them, with public/product-audit added to its targets
- new generic vitec:wrap-cards-grid --page=<slug>: wraps a page's cards
  into vitec_cols_33_33_33 containers (b13 columns 221/222/223), three
  per container, section-aware - every top-level intro paragraph starts
  a section, other element types close it, so the category headings on
  /products keep their grids beneath them. Existing grid containers are
  pooled and reused, surplus ones hidden
- the first apply aborted mid-move and left a half state (15 empty
  shells, three cards moved; diagnosis migrations/check_grid_447.php,
  kept) - v2 repairs such states by design: global container pool,
  hard asserts after the create pass, try/catch around the move pass,
  and a final "no card left top-level" verification
- applied on /products and verified live: 38 cards in 15 grids
  (sections 5/7/3/6/2/4/2/6/3 -> 2/3/1/2/1/2/1/2/1 containers), order
  intro -> grids -> banner -> faq intact, children delivered in the
  known items/contentElements shape

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 15:10:24 +01:00
orascheandClaude Fable 5 c8d653b39b Card images: product photos plus numbered placeholders on the new cards
- new vitec:attach-card-images (dry-run by default) for the ~145
  vitec_card elements the 2026-10-27 workbook imports created across
  /products, /insights, /support and /contact: a card linking
  /product/<slug> gets the record's first product photo (hero banner
  as fallback), every other card gets the numbered placeholder
  placeholders/pages/<pid>-NN.png counted from slot 10 in card order
  per page (slots 01-04 and 08 are taken by the page conventions; the
  number doubles as the image wish list towards the agency). 110
  placeholder PNGs (640x480) were generated from the live payloads so
  the numbering is deterministic; cards that already carry an image are
  never touched but keep advancing the counter, so re-runs stay stable
- container children are included: the editor had arranged the /support
  cards into a grid container (colPos 221-223/251), which the first
  top-level-only query skipped
- applied and verified live: 42 product photos, 110 numbered
  placeholders, 18 generic grey cards - the grey ones map the records
  without any image at all (ArtioSign, ArtioView, Arqa, the Aetria
  family, Wall Control 10, X-Series; all Datapath software without an
  image delivery, queued for Joe)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 14:26:15 +01:00
orascheandClaude Fable 5 c6147d020e Fill the /products portfolio page from the agency workbook
- new vitec:build-product-portfolio (dry-run by default): hero, nine
  category sections (intro paragraphs with the exact sitemap H2s and
  Learn-more buttons onto the new category pages), 38 product cards
  with record-first link resolution (30 resolved; the 8 open record
  gaps stay reported), a Case Studies & Events banner (midnight,
  success stories / events), ONE vitec_faq accordion whose four
  questions are created as vitec_faq_items collection children
  (field names probed against the schema at runtime), and the orange
  prefooter banner (contact / solutions); full page properties and
  page layout 0 -> 1 "Index Page"
- diagnosis first (new read-only migrations/check_products_page.php
  and check_products_page2.php): the dry run reported zero legacy
  elements although the payload rendered ~40 - every own element of
  page 447 was already soft-deleted, the visible rendering came from
  the "Collection Product Infos" compose page. Building in place was
  therefore safe; unconsumed visible legacy would have been hidden,
  not deleted
- side finding, left open on purpose: six old-taxonomy category stubs
  (#451-454, #456, #458) still sit visible under /products with the
  family pages beneath them - hide/move/redirect is an editorial
  decision queued for Olli
- applied and verified live (hero, 9 category buttons, 30 card links,
  4 FAQ items, both banner CTA pairs, meta)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 14:12:39 +01:00
orascheandClaude Fable 5 e2d4367d7a Products run: missing records, asset re-run, audit page, category pages
- new vitec:create-missing-products: MGW Diamond TOUGH, MGW Pico TOUGH,
  MGW Pico+ TOUGH and X-View Integrated Displays as visible records
  (teaser drafts, pid and sys_category assignments copied from the
  sibling product; titles equal the Dropbox folder names so the image
  importer matches immediately). IQS4 stays out - its delivery folder
  is empty, question to Joe
- asset re-run via vitec:import-product-images after re-staging the
  Dropbox delivery (the misfiled Diamond-OG banner corrected into the
  OG inbox folder again): new photos for 95-Series, Avedia chassis,
  SFF PRISM and the new TOUGH/X-View records
- new migrations/build_product_audit.php: generates the internal
  product-asset audit at /product-audit/ (same basic auth, same-origin
  thumbnails) - one row per record with thumbnail, detail link, badges
  for hero/photos/texts/resources/datasheets/solutions, flags, stats
  and client-side filters; re-run any time
- new vitec:import-product-category-pages: builds the nine
  /products/<category> pages (frontend layout 9, hero/main/preFooter)
  from the agency "Product Category Pages" workbooks - intro
  paragraphs, Why-VITEC / product / solution cards, the comparison
  rows as ONE text element with a plain table (no comparison CType
  exists; FE styles it), prefooter banner to /contact, full page
  properties, page sorting per sitemap 3.1-3.9; the empty shells #455
  and #457 are reused. Product card links resolve record-first (exact
  title, stripped parenthesis, family alias, unique slug prefix);
  ProductFamilyAliases gains ten category-level family labels. Solution
  cards tolerate US/UK spelling (center/centre, visualization/
  visualisation). Applied and verified live (nine routes, links,
  tables, meta)
- known gaps reported, queued for Joe: IQS4, Media Players (93/94),
  Chassis Systems, Express Backplanes, Accessories & Cables, PCIe
  Encoders, VMS Plugins & Integrations; the Vision family card awaits
  a flagship decision

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 13:31:03 +01:00
orascheandClaude Fable 5 8497dad06a Megamenu follow-ups: nav_hide honoured, entries insertable, page tiles
- getMenu() treats hidden/time/fe_group as enable fields but NOT
  nav_hide - the treeColumns doc comment claimed otherwise, and the
  news detail page ("article") stayed in the new spotlight rail after
  the editor hid it in menus. All three getMenu() calls (spotlight nav,
  pagetree columns and their items) now pass 'AND nav_hide = 0';
  verified live (6 rail items)
- all four tx_evomegamenujson_* tables get
  security.ignorePageTypeRestriction: they are inline children of the
  menu plugin and live on whatever page the plugin sits on - creating
  a new entry on the standard page failed with "table is not allowed"
- new vitec:attach-page-media --root=<slug> (dry-run by default):
  attaches the numbered placeholder pages/<pid>-08.png (slot 08 = page
  media / menu tile, the number doubles as the image wish list) as page
  media to every child page without one; existing media is never
  touched. Run for /support (8 pages) so the planned Support menu entry
  renders as two 4-tile rows via the pagetree layout's column images

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 12:08:26 +01:00
orascheandClaude Fable 5 90c2f97c5b Market records: delete the dropped legacy set; defuse the rename map
- vitec:align-market-records gains --delete-dropped: soft-deletes the
  records the agency dropped from the taxonomy, including their MM rows
  (DataHandler does not clean the foreign side on delete). Decision:
  all nine legacy records go, including Healthcare #42 - the Overlake
  story loses its badge on purpose and reports as unresolved on future
  importer runs
- the run exposed a regression: the RENAME_MAP entries Government ->
  Government & Agencies and Education -> Education Communications are
  not idempotent because both old names double as NEW main-market
  names. The re-run hijacked the main records (stories and teaser
  travelled along), created empty replacements and deleted the real
  sub-market records as unclaimed
- repair: migrations/fix_market_rename_regression.php (dry-run default,
  guarded) swaps the pairs back - main records keep their stories and
  workbook teasers, the fresh empties take over the sub-market roles;
  applied and verified live (marketlist 11/11 with main detail pages,
  badge distribution back to 20/9/9/8/6/4/1, no legacy names left)
- the two non-idempotent entries are removed from RENAME_MAP with a
  comment: a rename mapping whose old name is reused by the new
  taxonomy must not survive its one-time run

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 11:28:26 +01:00
orascheandClaude Fable 5 3b3ef470b0 Content pages: Insights / Support / Contact branches from the workbooks
- new vitec:build-content-pages (dry-run by default, --branch filter,
  --publish): fills all 21 content-page workbooks (Insights 7, Support 9,
  Contact 5, agency status "Draft for review") with embedded copy -
  the workbooks' instruction rows are deliberately not imported; two
  DataHandler passes (pages first, element links need real uids),
  explicit page sorting and element sequencing
- /insights became a hero page (layout 1) with six navigation cards;
  the news plugin moved onto the new /insights/news (layout 12); the
  standalone /events page moved under /insights (slug /insights/events,
  layout 19, redirect queued - note below)
- NEW /support branch (layout 11) with eight layout-16 subpages:
  HelpDesk login (external link + vitec_helpdeskform for access
  requests), support plans (Silver/Gold/Platinum cards), training plus
  the Aetria/VSN course pages, commissioning, supported devices,
  materials compliance (no dedicated form yet - routes to /contact)
- /contact became a hero page with six route cards; the form container
  (vitec_contactform) moved onto the new /contact/contact-form; new
  /contact/request-a-demo (vitec_demoform), /contact/locations
  (vitec_locationlist; three Datapath offices as HIDDEN location
  records pending confirmation) and /contact/showrooms (16 venues)
- unverifiable sections (sustainability claims, careers vacancies)
  are hidden editorial shells; full page-properties set everywhere
- applied with --publish and verified live (all 21 routes, forms,
  lists, moved plugins). Known follow-ups: /events answers 307 - an
  auto-created redirect row shadows the command's 301
  (migrations/fix_events_redirect.php shows and repairs it); the
  "Get Support" meta-nav page still points at /components-showcase;
  two CTA targets (support-plans datasheet, careers vacancies) are
  editorial
- Datapath stories stay parked ("do not infer records" per the
  Success Stories workbook) - documented, questions queued for Joe

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 11:03:33 +01:00
orascheandClaude Fable 5 c4ec979f78 Fill the /markets main page from "Markets Hero Page.xlsx"
- new vitec:import-markets-main (dry-run by default): one page with
  fixed approved copy, so the workbook content is embedded in the
  command instead of parsed at runtime; every target is resolved at
  runtime (pages by slug, elements by CType/colPos/container, market
  records by normalised title) and the command aborts on an unexpected
  structure
- the page already had the workbook structure (template vitec__2), so
  this updates the hero, the 50/50 markets intro (CTA -> /solutions)
  and the overview header in place
- the marketlist flexform selection moves from the old 9-record set
  (which still included the dropped "Security & Surveillance") to the
  11 main-market records in workbook order
- the 11 market card bodies land in the market records' teaser field
  (all were empty; the marketlist cards render from the records -
  frontend briefing asks for teaser on the cards)
- the old single-button prefooter container is hidden, replaced by a
  vitec_ctabanner (centered/orange, as on the 11 market hero pages)
  with both workbook CTAs: "Talk to our team" -> /contact and
  "Explore solutions" -> /solutions; explicit sorting after DataHandler
- page properties per the established set: seo/og/twitter title and
  description, canonical, abstract, curated keywords
- applied and verified live in the JSON (cards, order, teasers, CTA
  targets, meta tags); no spec bump - no new JSON keys involved

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-27 10:15:02 +01:00
orascheandClaude Fable 5 8adbe8cc90 Market record taxonomy aligned with the agency structure
- new vitec:align-market-records: the visible /markets page tree is the
  target (it implements the agreed agency taxonomy). RENAME_MAP carries
  16 records - story relations included - onto the new names, exact
  matches get their detail_page wired, 10 records are created (CNI,
  Government, Public Safety and seven sub-markets). Records the agency
  dropped are never touched: they are reported with their attached
  stories as the editorial decision list - which turned out empty, all
  nine carried zero stories
- INDUSTRY_TO_MARKET of the story importer moved to the new names. The
  old combined defense market split cleanly: the Government industry now
  maps to its own market, Military stays on Defense and Security, so the
  --markets-only re-run redistributed those stories by their original
  industry. Healthcare deliberately lost its target (market dropped) and
  surfaces as unresolved from now on
- re-run applied: 48/48 stories relinked, badges verified live (no old
  names left), Solr reindex queued - the market facet now speaks the new
  taxonomy

Open: Overlake Medical Center still points at the dropped Healthcare
record (title-match fallback), the nine orphaned old records await
deletion, Public Safety has no stories yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-26 10:40:42 +01:00
orascheandClaude Fable 5 6b0b2a3511 Engagament rename; redirects installed, CLI-queued and backfilled
- new generic vitec:rename-page --page=<uid|slug> --title="...": renames
  a page, recalculates its own slug from the parent slug plus the new
  title and cascades every descendant slug - the gap a backend rename
  leaves open. Used to fix the "Engagament" page (#465): title, own
  slug and five child slugs in one run, verified live (zero occurrences
  left in the JSON)
- SubsolutionWorkbookImporter::norm() tolerates the engagament spelling:
  the 39 delivered workbook metas carry the old page title, re-imports
  keep matching (verified)
- typo3/cms-redirects installed - it had NEVER been installed, so every
  "redirect follows" of the 10-23 renames was wishful thinking and all
  old URLs answered 404. Backend slug changes now auto-create redirects
- EXT:redirects' auto-creation does not fire for CLI DataHandler runs,
  so vitec:rename-page, vitec:normalize-slugs and
  vitec:align-market-pages now queue their 301 sys_redirect rows
  themselves in the same DataHandler run
- new one-off vitec:backfill-redirects: the 28 paths vacated on
  2026-10-23/26, reconstructed from the command protocols, guarded
  against existing redirects and live slugs; applied and verified.
  Note the headless semantics: a redirect arrives as the JSON envelope
  {"redirectUrl": ..., "statusCode": 301} with HTTP 200 - the front end
  must act on redirectUrl (briefing updated)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-26 10:19:16 +01:00
orascheandClaude Fable 5 cbe46cdba2 Megamenu page-tree panels; generic slug normaliser
- evo_megamenu_json: new entry layout "pagetree" - the panel columns
  mirror the live subtree of the page the entry links to (one child per
  column, grandchildren as items, getMenu() restrictions apply), so the
  menu can never age against the page structure again. The stale
  9-market panel after the markets realign was the trigger. Curated
  columns are ignored on this layout; teasers and the featured slot
  stay editorial. Same columns shape in the payload - front ends need
  no change beyond knowing the new layout string
- new vitec:normalize-slugs --root=<slug> [--style=plain|and]:
  recursively realigns every slug below a root to
  <parent-slug>/<slugified-title>, cascading, with a sibling-collision
  warning; EXT:redirects covers the old URLs. TYPO3's slug generator
  keeps slashes from page titles (third hit: rtcc-/-rtic), this sweeps
  a whole tree. Run on /solutions: four fixes, including the
  long-standing passenger page and solution page 467. The misspelled
  "engagament" slug stays deliberately - it follows the page title and
  is fixed in the coordinated typo task

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-23 14:10:57 +02:00
orascheandClaude Fable 5 303defe24d Markets branch: 11-market tree, sub-market import, hero pipeline
Structure per the agency agreement (Joe/Christina, confirmed 2026-10-23):
/markets -> eleven market hero pages -> their sub-market pages; the
use-case level stays sections on the sub-market pages.

- new vitec:align-market-pages: renames 8 main market pages onto the
  agency names (slug change -> EXT:redirects), creates the 3 new markets,
  hides the dropped "Security & Surveillance" (records/stories
  untouched), reports unknown children. A child-slug pass realigns every
  child to <parent-slug>/<slugified-title> (DataHandler does not cascade
  slug changes; this also repaired the third embedded-slash slug,
  rtcc-/-rtic). --publish unhides the tree (dev convenience; element
  shells stay hidden)
- sub-market import: all 24 agency workbooks applied (4 existing pages,
  20 created) - their Meta sheets were clones of Corporate Environments
  (record_uid 32) and were repaired beforehand (market/sub-market set,
  record_uid 0; the record taxonomy alignment is a separate step)
- SubmarketWorkbookImporter: page-properties parity (og/twitter titles
  and descriptions, canonical, abstract from the intro copy) and a
  placeholder upgrade pass - pages created in the same run only get the
  generic grey (their uid is still NEW), the re-run swaps it for the
  numbered per-page placeholder
- new market hero pipeline: frontend layout 23 "Market Hero Page"
  (zones hero=colPos1, main=colPos0), Import\MarketHeroPageImporter +
  vitec:import-market-hero-workbooks (inbox migrations/markethero_xlsx_in,
  gitignored). Hero H1 travels on the introparagraph header (markethero
  has no headline field), the hero CTA anchors #submarkets, sub-market
  cards link their child page via t3://page, customerlogos and
  usecaselist ship hidden until filled editorially, prefooter ctabanner;
  full page-properties set with 11 curated keyword sets
- applied end to end and live-verified (layout 23 heroes with linked
  cards, layout 21 sub pages with all six colPos groups, clean slugs)
- spec v1.25

Open: market RECORD taxonomy (story badges / Solr facets still use the
old 9 mains), featured product "ISR Video Mission Commander" has no
record, the /markets main page workbook (template 2) is a separate flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-23 13:52:43 +02:00
orascheandClaude Fable 5 115952a132 Product banners and photos: vitec:import-product-images
- new command attaching the agency asset delivery (one Dropbox folder
  per product) to the product records: files containing "banner" go to
  heroimage (first reference reused/swapped), every other jpg/png -
  carousel subfolders included - is APPENDED to productimage with the
  full reference list, so the live-site images from 2026-09-04 stay
- folder -> record matching: normalised title, title without the
  parenthetical, slug, then IMAGE_ALIASES (Joe's "+1" Vision folder
  names map onto the existing records - their banner files still carry
  the old product names; 95-Series -> avedia-end-points)
- uploads land in fileadmin/product_import/<slug>/ with REPLACE, so
  re-runs are idempotent; inbox migrations/product_images_in/ gitignored
- applied: 39 banners + 14 photos across 42 products, 0 errors,
  live-verified (srcset/webp processing included)
- the "MGW Diamond (SDI encoder)" Dropbox folder held the OG blade's
  banner (image inspected): moved to the OG product in the inbox, the
  appliance deliberately keeps no hero; the Dropbox source still has it
  in the wrong folder
- still without records: MGW Diamond/Pico/Pico+ TOUGH and X-View - their
  assets wait in the inbox, a re-run attaches them once records exist

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-23 13:05:32 +02:00
orascheandClaude Fable 5 3e2ebf3823 Solution group pages: layout 22 + Template-06 workbook import
- new frontend layout 22 "Solution Group Page" (provider LAYOUT_MAP,
  TCA layout item, locallang_pages.xlf; zones hero=colPos1, main=colPos0
  like layout 5 so the front end reuses its components)
- new pipeline Import\SolutionGroupPageImporter +
  vitec:import-solution-group-workbooks (inbox
  migrations/solutiongroups_xlsx_in/, gitignored): reads the agency
  "Template 06" group workbooks by column-A label scan, resolves the
  group via "Hero H1" -> is_group record -> detail_page (pages must
  exist), cursor-based slotNext() allows repeated CTypes per column
- element plan per page: markethero (+#capabilities CTA anchor) + hero
  intro paragraph; intro + supporting 50/50; challenges header + 4
  text_only cards; capabilities header + 4 alternating horizontal cards
  (bullets as <ul>); usecaselist shipped HIDDEN (an empty selection
  would list ALL stories - editors pick, then unhide); marketinsights;
  prefooter ctabanner. Page properties as on the sub-solution pages,
  8 curated keyword sets
- 56 numbered grey placeholders for pages 459-466 (7 slots per page)
- applied to all 8 group pages, live-verified on 459/465; the solutions
  branch is now content-complete (groups + 39 details)
- spec v1.24 (revision row, head/footer)

Found on the way: the "Engagament" typo lives in page 465's title AND
slug; the record is spelled correctly. Fix needs coordination with the
sub-solution workbook metas - own task, not done here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-23 12:19:05 +02:00
orasche 8e67f5db23 Sub-solution workbook pipeline on layout 5, fix element order in both importers
- New, deliberately separate pipeline for sub-solutions (markets and
  solutions must never break each other): generator
  vitec:generate-subsolution-workbooks (list from the page tree below
  /solutions, 39 workbooks, every sub-solution matched to its record),
  SubsolutionWorkbookImporter + CLI vitec:import-subsolution-workbooks,
  and a "Sub-Solutions" tab in the VITEC Import module with its own
  inbox migrations/subsolutions_xlsx_in/
- Targets the existing template 5 "Solution Detail" without changing
  it: hero element in colPos 1, the remaining sections in fixed order in
  colPos 0, found again by CType on re-import; existing empty sub-solution
  pages are switched to layout 5, missing ones created hidden;
  detail_page set on the solution record
- Workbook per the Figma "07 Solution Detail page": hero without video
  teaser, intro with headline and without learn-more/portfolio, extended
  icon list, own page-layout reference image
- Shared content elements: market hero/intro/insights relabelled
  "Market/Solution", market intro gains the optional intro_headline
  field (new column, requires extension:setup)
- Fix: DataHandler puts every NEW record with a positive pid at the top
  of its column, so pages built in one run came out reversed. Both
  importers now track the page order per colPos and write
  tt_content.sorting explicitly after the DataHandler run; re-applying
  repairs pages imported earlier (verified on the control-room
  sub-solution and sub-market page 443)
- migrations/fill_test_subsolution.php: realistic test content from
  vitec.com copy and images for the control-room sub-solution
- Docs section 5z
2026-10-07 15:39:18 +02:00
orasche 3d5459b8d2 Sub-market workbook import as a backend module tab
- 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)
2026-10-05 15:35:40 +02:00
orasche 8b175211ea vitec:generate-submarket-workbooks builds one protected customer workbook
per sub-market (25, grouped by the 9 main markets): VITEC-styled Read me,
a Page-layout sheet embedding the Figma design with section markers,
DB-live dropdowns, soft character limits, a hidden Meta sheet and named
input cells. Names must NOT start with a cell-reference token - Excel
silently strips s1_-style names as repairs.

vitec:import-submarket-workbooks (dry-run default, idempotent) finds or
creates the page below its main market (hidden, layout 21), fills the six
colPos slots updating in place and never deleting, links detail_page on
the market record, downloads asset URLs into fileadmin/submarket_import/
for editorial review and writes the flexforms as DataHandler arrays.
Reading is three-layered after a bruising day of real-world Excel: named
ranges first, then a label scan over the locked column A with section
tracking - a customer save stripped ALL defined names and shifted rows,
so fixed addresses are not a contract.

Learned and fixed along the way: DuplicationBehavior moved to
Resource\Enum in v14; sys_file_reference rows via DataHandler need
pid/tablenames/uid_foreign or are dropped silently; handing DataHandler a
fresh NEW reference per run STACKS live references on re-import (caught
by a double-run test), so attachments are now content-idempotent - the
existing live reference is reused or retargeted and surplus duplicates
are deleted via cmdmap; RequestFactory replaces GeneralUtility::getUrl so
download failures carry their reason - which surfaced that the dev basic
auth blocked the server itself (server IP + loopback now allowlisted,
which a future SSR needs anyway) and that the core resource-consistency
check rightly rejects HTML masquerading as PNG.

Verified end to end on Broadcast & Media from a clean slate, twice in a
row: 12 elements in colPos 110-160, layout-21 payload, both images
attached exactly once with publicUrl in the JSON; second run reports
nothing but unchanged.
2026-10-02 13:24:54 +02:00
orasche f0b0694842 - New extension fe_comment (Evomedien, framework-agnostic): floating page
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
2026-09-30 13:59:55 +02:00
orascheandClaude Fable 5 6961254e57 Workbook import phase 1: manifest-driven batch command
Implements the 18.09. strategy's phase 1 on top of the existing single
importer (which stays the one write path):

- new vitec:import-product-workbooks: runs vitec:import-product-workbook
  per manifest entry (in-process sub-command), aggregates a per-file
  report (writes/unchanged/na, URL-mismatch and error flags, details on
  -v or failure) plus a summary; without --apply every run is a dry
  run; --only filters
- migrations/products_xlsx/ now holds working copies of all 100 agency
  workbooks in their category folders (git-ignored) plus the TRACKED
  manifest.json: workbook path -> product slug, family entries mark the
  K-level landing pages (never imported), product null = unresolved.
  Generated from the 18.09. analysis + sitemap column K:
  68 product / 17 family / 15 open
- --init-manifest resolves open entries against the full product table
  (hidden records included): URL segment/filename vs slug, normalized
  title, unique slug prefix; only unambiguous hits are written back
- single command compare is now tolerant of the RTE storage transform
  (whitespace collapse + <p>-wrap simulation), fixing the known
  every-run-WRITE false positive on re-imports

Post-dry-run hardening, applied the same day (69 products, 785 field
writes across two runs, zero errors, verified against the live JSON):

- PROTECTED_FIELDS title/urltitle in the single command: the stored
  module mapping would have overwritten six canonical sitemap-V2
  product titles
- the stored mapping predated the 18.09. defaults and silently dropped
  `highlights` on every product; the single command now merges
  DEFAULT_MAPPING under the stored mapping (defaults as base, stored
  as per-field override)
- known limitation for phase 2: the g: composed fields report WRITE on
  every run (RTE transform alters ul/strong markup beyond the p-wrap
  normalisation) - the change watch must compare against stored
  imported values, not the database

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-24 15:50:16 +02:00
orascheandClaude Fable 5 6811463343 Product texts: mapping completed; product model: solutions + resources
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>
2026-09-24 14:47:04 +02:00
orasche 5bb4e374b4 Add evo_megamenu_json extension and seed the megamenu
- new project-neutral extension evo_megamenu_json (Evomedien): a Megamenu
  plugin holding entries, columns, items, teaser cards and a featured slot
  as nested records; presentation settings travel with the payload so the
  front end reads behaviour instead of hard-coding it; published as
  page.10.fields.megaMenu, driven by the site settings megamenu.contentUid
  and megamenu.storagePid
- vitec:seed-megamenu fills one element from the live structure: 5 entries,
  26 columns, 95 items, 4 story cards; items without a page yet are flagged
  pending so the front end falls back to the column link
- vitec:debug-sets prints the resolved site set order
- read-only diagnostics: check_megamenu_schema.php, check_typoscript_templates.php
- docs: schema updates run via extension:setup (database:updateschema no
  longer exists in v14), and a JSON Accept header activates headless-mixed,
  which strips every set-added page field
2026-09-11 11:44:20 +02:00
orasche e9eaa62d89 Align solutions to the sitemap's 39 Generic Solutions; map detail pages
- vitec:align-solutions: declarative target = the 39 Generic Solutions in 8
  group categories under the Solution root (sheet is binding, group typo
  Engagament->Engagement fixed). Two legacy records renamed onto their sheet
  names instead of deleted (keeps the only editorial teaser/description),
  slugs generated for all, market categories stripped from solutions, the
  ~82 market-specific records soft-deleted (markets link editorially)
- vitec:map-solution-pages: writes matching page uids into detail_page
  (unique matches, fill-only); disambiguates the duplicated page branches
  by slug suffix and prunes the 10 empty duplicate pages
- read-only diagnostics: check_solutions.php, check_dup_solution_pages.php
2026-09-04 15:30:27 +02:00
orasche c91063d6f3 Import product photos from live sites; link downloads and SEO meta to products
- vitec:import-product-images: attach staged photos (fileadmin/user_upload/
  products/<slug>/, 116 files harvested from vitec.com and datapath.co.uk
  carousels) to image-less products via FAL/DataHandler; fill-up only.
  fix_image_reference_fieldname.php repairs the first run's references
  (TCA field is productimage, not the JSON key images)
- vitec:link-product-downloads: match the download records imported on
  2026-08-06 to products by title (doc-type/brand/role suffix stripping,
  alias table for live-site naming deviations, series rules for g45xx/e38xx/
  chassis docs); 55 unique-match links added, add-only, idempotent
- SEO meta: seo_meta_vitec.json + apply_seo_meta.php fill empty seotitle/
  seometa from the live vitec.com product pages (11 products, fill-up only)"
2026-09-04 14:34:38 +02:00
orasche 5499ca54af Align product records with sitemap V2; product family model; CLI workbook import
- 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
2026-09-04 12:59:17 +02:00
orasche 7442d10e68 SEO record links; story markets repair; story page titles
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
2026-08-26 19:48:43 +02:00
orasche cf7a04ea15 Added Downloads to search index. 2026-08-21 15:44:03 +02:00
orasche 710f95794c Add Solr search: EXT:solr 14 integration and JSON search endpoint
Apache Solr 10 runs on a dedicated VPS behind a Caddy HTTPS proxy
(vitecsolr.evomedien.de) because the managed webserver only allows
outgoing standard ports. Credentials stay out of git: the site config
carries %env()% placeholders resolved via putenv() in the git-ignored
config/system/additional.php.

- composer: apache-solr-for-typo3/solr 14.0.0-RC1 (the only line
  compatible with TYPO3 14; final 14.0.0 will arrive via composer update)
- config.yaml: read connection https/443, core_en per language,
  env placeholder credentials; .gitignore covers additional.php
- setup.typoscript: config.index_enable = 1 (page indexing silently
  refuses without it), solr_pi_results JSON renderer registration,
  results highlighting, and content field extraction from tt_content
  rows - the headless JSON output has no TYPO3SEARCH markers, so the
  default page content extraction indexed an empty content field
- SearchJsonRenderer (renderer #27): JSON output for the search plugin
  on /search. GET q/page in; { query, page, resultsPerPage, numFound,
  totalPages, results[], suggestions } out. Disables the page cache per
  request: config.no_cache is gone in TYPO3 v14, and q/page are
  excluded from cHash, so cached variants would collide
- ext_localconf.php: q and page added to cacheHash excludedParameters
- SolrIndexCommand (vitec:solr-index): works the index queue from the
  CLI with connection diagnostics and a --debug single-step mode -
  EXT:solr 14 ships no console commands and the backend button indexes
  one item per click
2026-08-21 14:43:44 +02:00