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>
This commit is contained in:
1 parent
80f6da7949
commit
d87e64f73b
12 files changed
+763
-2
No files matched your search
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Comparison report: download records vs. the delivered product workbooks
|
||||
* (request Olli 2026-10-28). Per product: linked downloads
|
||||
* (tx_vitec_product_download_mm, uid_local = product), datasheet coverage
|
||||
* (download title suffix "datasheet" or a Datasheet category), the manual
|
||||
* Resources-tab selection (tx_vitec_product_datasheet_mm) - matched against
|
||||
* the workbook coverage from migrations/products_xlsx/manifest.json.
|
||||
*
|
||||
* READ-ONLY. Usage: php migrations/check_product_downloads.php
|
||||
*/
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
$root = null;
|
||||
$dir = __DIR__;
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
if (is_file($dir . '/config/system/settings.php')) { $root = $dir; break; }
|
||||
$dir = dirname($dir);
|
||||
}
|
||||
if ($root === null) { exit("no project root\n"); }
|
||||
$settings = require $root . '/config/system/settings.php';
|
||||
$db = $settings['DB']['Connections']['Default'];
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', $db['host'] ?? 'localhost', $db['port'] ?? 3306, $db['dbname'] ?? ''),
|
||||
$db['user'] ?? '', $db['password'] ?? '',
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||||
);
|
||||
|
||||
// --- workbook coverage from the manifest ------------------------------------
|
||||
$manifest = json_decode((string)file_get_contents($root . '/migrations/products_xlsx/manifest.json'), true) ?: [];
|
||||
$workbookSlugs = [];
|
||||
foreach (($manifest['workbooks'] ?? []) as $file => $entry) {
|
||||
if (!empty($entry['product'])) {
|
||||
$workbookSlugs[(string)$entry['product']] = basename((string)$file);
|
||||
}
|
||||
}
|
||||
|
||||
// --- data ---------------------------------------------------------------------
|
||||
$products = $pdo->query('SELECT uid, title, slug, hidden, legacy, supportproduct, subproduct FROM tx_vitec_domain_model_product WHERE deleted = 0 ORDER BY title')->fetchAll();
|
||||
$downloads = $pdo->query('SELECT uid, title, hidden FROM tx_vitec_domain_model_download WHERE deleted = 0')->fetchAll();
|
||||
$downloadTitle = array_column($downloads, 'title', 'uid');
|
||||
|
||||
$linksByProduct = [];
|
||||
$linkedDownloadUids = [];
|
||||
foreach ($pdo->query('SELECT uid_local, uid_foreign FROM tx_vitec_product_download_mm') as $r) {
|
||||
$linksByProduct[(int)$r['uid_local']][] = (int)$r['uid_foreign'];
|
||||
$linkedDownloadUids[(int)$r['uid_foreign']] = true;
|
||||
}
|
||||
$sheetsByProduct = [];
|
||||
foreach ($pdo->query('SELECT uid_local, COUNT(*) c FROM tx_vitec_product_datasheet_mm GROUP BY uid_local') as $r) {
|
||||
$sheetsByProduct[(int)$r['uid_local']] = (int)$r['c'];
|
||||
}
|
||||
|
||||
$isDatasheet = static function (string $title): bool {
|
||||
return (bool)preg_match('/data\s*sheet\s*$/i', trim($title));
|
||||
};
|
||||
$docType = static function (string $title): string {
|
||||
foreach (['datasheet', 'data sheet', 'brochure', 'user guide', 'quick start guide', 'application note',
|
||||
'white paper', 'case study', 'success story', 'manual', 'firmware', 'software', 'flyer', 'guide'] as $suffix) {
|
||||
if (preg_match('/' . preg_quote($suffix, '/') . '\s*$/i', trim($title))) {
|
||||
return $suffix;
|
||||
}
|
||||
}
|
||||
return '(no type suffix)';
|
||||
};
|
||||
|
||||
// --- per-product evaluation ------------------------------------------------------
|
||||
$noDownloads = [];
|
||||
$noDatasheet = [];
|
||||
$okCount = 0;
|
||||
$withoutWorkbook = [];
|
||||
foreach ($products as $p) {
|
||||
$uid = (int)$p['uid'];
|
||||
$slug = (string)$p['slug'];
|
||||
$linked = $linksByProduct[$uid] ?? [];
|
||||
$hasSheet = false;
|
||||
foreach ($linked as $dUid) {
|
||||
if ($isDatasheet((string)($downloadTitle[$dUid] ?? ''))) { $hasSheet = true; break; }
|
||||
}
|
||||
$flag = ((int)$p['legacy'] ? ' [legacy]' : '') . ((int)$p['hidden'] ? ' [hidden]' : '')
|
||||
. ((int)$p['supportproduct'] ? ' [support]' : '') . ((int)$p['subproduct'] ? ' [sub]' : '');
|
||||
$line = sprintf('#%-4d %-45s dl=%-2d sheetsTab=%d%s', $uid, $p['title'], count($linked), $sheetsByProduct[$uid] ?? 0, $flag);
|
||||
if (!isset($workbookSlugs[$slug])) {
|
||||
$withoutWorkbook[] = $line;
|
||||
}
|
||||
if (isset($workbookSlugs[$slug])) {
|
||||
if ($linked === []) { $noDownloads[] = $line; }
|
||||
elseif (!$hasSheet) { $noDatasheet[] = $line; }
|
||||
else { $okCount++; }
|
||||
}
|
||||
}
|
||||
|
||||
// --- orphan downloads ---------------------------------------------------------------
|
||||
$orphans = [];
|
||||
$orphanTypes = [];
|
||||
foreach ($downloads as $d) {
|
||||
if (!isset($linkedDownloadUids[(int)$d['uid']])) {
|
||||
$type = $docType((string)$d['title']);
|
||||
$orphanTypes[$type] = ($orphanTypes[$type] ?? 0) + 1;
|
||||
$orphans[] = $d['title'];
|
||||
}
|
||||
}
|
||||
|
||||
printf("Produkte: %d (davon %d mit Workbook) · Downloads: %d (davon %d verlinkt, %d verwaist)\n\n",
|
||||
count($products), count($workbookSlugs), count($downloads), count($linkedDownloadUids), count($orphans));
|
||||
|
||||
echo "=== A) Workbook-Produkte OHNE verknuepfte Downloads (" . count($noDownloads) . ") ===\n";
|
||||
foreach ($noDownloads as $l) { echo " $l\n"; }
|
||||
|
||||
echo "\n=== B) Workbook-Produkte mit Downloads, aber OHNE Datasheet (" . count($noDatasheet) . ") ===\n";
|
||||
foreach ($noDatasheet as $l) { echo " $l\n"; }
|
||||
|
||||
echo "\n=== C) Workbook-Produkte mit Datasheet-Abdeckung: $okCount ===\n";
|
||||
|
||||
echo "\n=== D) Produkte OHNE Workbook (" . count($withoutWorkbook) . ") - zur Einordnung ===\n";
|
||||
foreach ($withoutWorkbook as $l) { echo " $l\n"; }
|
||||
|
||||
arsort($orphanTypes);
|
||||
echo "\n=== E) Verwaiste Downloads nach Typ ===\n";
|
||||
foreach ($orphanTypes as $type => $n) { printf(" %-22s %d\n", $type, $n); }
|
||||
echo "\n Beispiele (max 30):\n";
|
||||
foreach (array_slice($orphans, 0, 30) as $t) { echo " - $t\n"; }
|
||||
Reference in new issue
Block a user