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>
This commit is contained in:
orascheandClaude Fable 5 committed 2026-10-26 10:19:16 +01:00
1 parent b1357a6ffe
commit 6b0b2a3511
7 files changed
+940 -489

No files matched your search

@@ -114,6 +114,7 @@ final class AlignMarketPagesCommand extends Command
'slug' => $joeSlug,
];
$targets[] = ['uid' => (int)$old['uid'], 'slug' => $joeSlug];
$this->queueRedirect($datamap, (string)$old['slug'], (int)$old['uid']);
$output->writeln(sprintf(' ~ %-36s RENAME from "%s" (uid %d), slug %s -> %s (redirect follows)',
$joeName, $oldName, (int)$old['uid'], $old['slug'], $joeSlug));
continue;
@@ -193,6 +194,7 @@ final class AlignMarketPagesCommand extends Command
$expected = rtrim((string)$target['slug'], '/') . '/' . $this->slugify((string)$child['title']);
if ((string)$child['slug'] !== $expected) {
$datamap['pages'][(int)$child['uid']]['slug'] = $expected;
$this->queueRedirect($datamap, (string)$child['slug'], (int)$child['uid']);
$output->writeln(sprintf(' ~ child slug: "%s" (uid %d) %s -> %s (redirect follows)',
(string)$child['title'], (int)$child['uid'], (string)$child['slug'], $expected));
}
@@ -218,6 +220,27 @@ final class AlignMarketPagesCommand extends Command
return Command::SUCCESS;
}
/**
* Queues the 301 for a vacated slug in the same DataHandler run -
* EXT:redirects' automatic creation does not fire for CLI DataHandler
* runs (found 2026-10-26: every renamed URL answered 404).
*
* @param array<string,array<int|string,array<string,mixed>>> $datamap
*/
private function queueRedirect(array &$datamap, string $oldSlug, int $pageUid): void
{
$oldSlug = rtrim($oldSlug, '/');
if ($oldSlug === '') {
return;
}
$datamap['sys_redirect']['NEWR' . substr(md5($oldSlug), 0, 10)] = [
'pid' => 0,
'source_host' => '*',
'source_path' => $oldSlug,
'target' => 't3://page?uid=' . $pageUid,
'target_statuscode' => 301,
];
}
private function norm(string $value): string
{
return (string)preg_replace('/[^a-z0-9]+/', '', str_replace(['+', '&'], ['plus', 'and'], mb_strtolower($value)));