Files
VITEC-website/migrations/fix_download_record_links.php
T
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

63 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
/**
* Repairs the record links the success-story import wrote into RTE bodies:
* it used t3://record?identifier=tx_vitec_download&uid=N (the TABLE name),
* but the configured link handler key - and therefore the only identifier
* config.recordLinks resolves - is `download`. Result: raw t3://record
* hrefs leaked into the JSON payload ("Download Success Story" buttons,
* found 2026-10-28).
*
* This migration rewrites identifier=tx_vitec_download -> identifier=download
* in tt_content.bodytext (plain and &amp;-encoded variants). One-time,
* idempotent, dry-run by default.
*
* Usage: php migrations/fix_download_record_links.php (dry run)
* php migrations/fix_download_record_links.php --apply
*/
error_reporting(E_ALL);
ini_set('display_errors', '1');
$apply = in_array('--apply', $argv, true);
$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]
);
$needle = 'identifier=tx_vitec_download';
$rows = $pdo->query(
"SELECT uid, pid, CType, LEFT(header, 40) h FROM tt_content
WHERE deleted = 0 AND bodytext LIKE '%identifier=tx_vitec_download%' ORDER BY pid, uid"
)->fetchAll();
echo ($apply ? "APPLY\n" : "DRY-RUN\n");
echo count($rows) . " element(s) carry the legacy identifier:\n";
foreach ($rows as $r) {
printf(" #%-6d pid=%-5d %-18s %s\n", $r['uid'], $r['pid'], $r['CType'], $r['h']);
}
if (!$apply) {
exit("DRY RUN - add --apply to rewrite identifier=tx_vitec_download -> identifier=download.\n");
}
$stmt = $pdo->prepare(
"UPDATE tt_content SET bodytext = REPLACE(bodytext, 'identifier=tx_vitec_download', 'identifier=download'),
tstamp = UNIX_TIMESTAMP() WHERE uid = ?"
);
foreach ($rows as $r) {
$stmt->execute([(int)$r['uid']]);
}
echo count($rows) . " element(s) rewritten. Flush the cache, then re-check a story payload.\n";