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
This commit is contained in:
2026-09-11 11:44:20 +02:00
parent e9eaa62d89
commit 5bb4e374b4
31 changed files with 2251 additions and 4 deletions

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* Read-only: is the evo_megamenu_json schema in place?
* Checks the four menu tables and the inline column on tt_content.
*
* Usage: php migrations/check_megamenu_schema.php
*/
$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("Could not locate project root above " . __DIR__ . "\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]
);
$tables = [
'tx_evomegamenujson_entry',
'tx_evomegamenujson_column',
'tx_evomegamenujson_item',
'tx_evomegamenujson_teaser',
];
$missing = 0;
foreach ($tables as $table) {
$exists = (bool)$pdo->query("SHOW TABLES LIKE " . $pdo->quote($table))->fetchColumn();
$count = $exists ? (int)$pdo->query("SELECT COUNT(*) FROM `$table`")->fetchColumn() : 0;
printf("%-32s %s%s\n", $table, $exists ? 'OK' : 'FEHLT', $exists ? " ($count Datensaetze)" : '');
$missing += $exists ? 0 : 1;
}
$col = $pdo->query("SHOW COLUMNS FROM tt_content LIKE 'tx_evomegamenujson_entries'")->fetch();
printf("%-32s %s\n", 'tt_content.tx_..._entries', $col ? 'OK' : 'FEHLT');
$missing += $col ? 0 : 1;
$ce = (int)$pdo->query("SELECT COUNT(*) FROM tt_content WHERE deleted = 0 AND CType = 'evomegamenujson_megamenu'")->fetchColumn();
printf("%-32s %d\n", 'Megamenu-Elemente angelegt', $ce);
echo "\n" . ($missing === 0
? "Schema vollstaendig - Plugin kann angelegt werden.\n"
: "$missing Objekt(e) fehlen - Schema-Update noetig.\n");

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/**
* Read-only: TypoScript template records and their static includes.
* A static include that pulls headless in AFTER the site sets resets the
* whole `page` object and silently drops page-level fields.
*
* Usage: php migrations/check_typoscript_templates.php
*/
$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]
);
$rows = $pdo->query(
"SELECT uid, pid, title, root, clear, hidden, deleted, include_static_file, tstamp,
LENGTH(config) AS config_len, LENGTH(constants) AS const_len
FROM sys_template ORDER BY pid, sorting"
)->fetchAll();
echo "=== sys_template ===\n";
foreach ($rows as $r) {
printf("uid %d | pid %d | %s%s%s%s | geaendert %s\n",
$r['uid'], $r['pid'], $r['title'],
$r['root'] ? ' | ROOT' : '',
$r['hidden'] ? ' | HIDDEN' : '',
$r['deleted'] ? ' | DELETED' : '',
date('Y-m-d H:i', (int)$r['tstamp'])
);
printf(" clear=%s config=%d Zeichen constants=%d Zeichen\n", $r['clear'], $r['config_len'], $r['const_len']);
$inc = trim((string)$r['include_static_file']);
if ($inc !== '') {
echo " include_static_file:\n";
foreach (explode(',', $inc) as $i => $one) {
printf(" %2d. %s\n", $i + 1, trim($one));
}
} else {
echo " include_static_file: (leer)\n";
}
}
// Anything in the config field that resets page?
foreach ($rows as $r) {
if ((int)$r['config_len'] === 0) { continue; }
$cfg = $pdo->query("SELECT config FROM sys_template WHERE uid = " . (int)$r['uid'])->fetchColumn();
$hits = [];
foreach (preg_split('/\R/', (string)$cfg) as $n => $line) {
if (preg_match('/^\s*page\s*[<=]/', $line) || str_contains($line, 'lib.headlessPage')) {
$hits[] = ($n + 1) . ': ' . trim($line);
}
}
if ($hits !== []) {
echo "\n=== page-Zuweisungen im config von uid {$r['uid']} ===\n" . implode("\n", $hits) . "\n";
}
}