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
- ' 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>
This commit is contained in:
orascheandClaude Fable 5 committed 2026-11-03 18:29:56 +01:00
1 parent 9211c8a395
commit 77661bf0b6
22 files changed
+2103 -63

No files matched your search

@@ -85,15 +85,57 @@ final class SubmarketWorkbookImporter
throw new \RuntimeException('Meta sheet missing - not a generated sub-market workbook?');
}
// Meta sheets can be stale template copies (2026-10-05: all 24 shared
// record 32; 2026-10-28: "Broadcast & Media.xlsx" still shipped the
// complete Corporate meta block). The FILENAME names the agency's
// actual intent - when it disagrees with the sheet, trust it, drop
// the copied record link and re-resolve the record by title.
$fileTitle = (string)preg_replace('/\.xlsx$/i', '', basename($file));
if ($this->norm($fileTitle) !== $this->norm($submarket)) {
$this->report[] = 'META REPAIR: sheet says "' . $submarket . '" (group "' . $group . '", record ' . $recordUid
. ') - stale template copy, using filename "' . $fileTitle . '"';
$submarket = $fileTitle;
$group = '';
$recordUid = 0;
foreach (GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_vitec_domain_model_market')
->fetchAllAssociative('SELECT uid, title FROM tx_vitec_domain_model_market WHERE deleted = 0') as $marketRow) {
if ($this->norm((string)$marketRow['title']) === $this->norm($submarket)) {
$recordUid = $recordUid === 0 ? (int)$marketRow['uid'] : -1; // -1 = ambiguous
}
}
$recordUid = max(0, $recordUid);
if ($recordUid > 0) {
$this->report[] = 'market record re-resolved by title: #' . $recordUid;
}
}
// --- resolve pages ---------------------------------------------------
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
$marketsRoot = $connection->fetchAssociative("SELECT uid FROM pages WHERE deleted = 0 AND slug = '/markets'");
if (!$marketsRoot) {
throw new \RuntimeException('Page /markets not found.');
}
$mainPage = $this->childByTitle((int)$marketsRoot['uid'], $group);
$mainPage = $group !== '' ? $this->childByTitle((int)$marketsRoot['uid'], $group) : null;
if ($mainPage === null) {
throw new \RuntimeException('Main market page "' . $group . '" not found below /markets.');
// repaired meta (or unknown group): locate the ONE main market
// that already has a sub-page with this title
$matches = [];
foreach ($connection->fetchAllAssociative(
'SELECT uid, title FROM pages WHERE deleted = 0 AND pid = ? AND sys_language_uid IN (0,-1)',
[(int)$marketsRoot['uid']]
) as $mainRow) {
if ($this->childByTitle((int)$mainRow['uid'], $submarket) !== null) {
$matches[] = $mainRow;
}
}
if (count($matches) === 1) {
$mainPage = $this->childByTitle((int)$marketsRoot['uid'], (string)$matches[0]['title']);
$this->report[] = 'group resolved via page tree: "' . $matches[0]['title'] . '"';
}
}
if ($mainPage === null) {
throw new \RuntimeException('Main market page "' . $group . '" not found below /markets'
. ($group === '' ? ' (and no main market has a sub-page titled "' . $submarket . '")' : '') . '.');
}
$subPage = $this->childByTitle((int)$mainPage['uid'], $submarket);