Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Controllers\ApiController;
/**
* Deckt das benutzerdefinierte Menü-Layout ab (Einstellung nav_layout).
*
* Schwerpunkte: serverseitige Normalisierung der Editor-Eingabe (nur bekannte
* Struktur, begrenzte Längen, keine doppelten Menüpunkte) und die Verkabelung
* von Speicher-Endpunkt, Render-Daten und Frontend-Layout.
*/
return static function (): void {
$normalize = function (string $raw): ?string {
$ref = new ReflectionClass(ApiController::class);
$ctrl = $ref->newInstanceWithoutConstructor();
$m = $ref->getMethod('normalizeNavLayout');
$m->setAccessible(true);
return $m->invoke($ctrl, $raw);
};
test('Gültiges Layout wird normalisiert übernommen', function () use ($normalize) {
$out = $normalize('{"groups":[{"label":" Meine Tools ","items":["tickets","vault"]},{"label":"","items":["topics"]}]}');
assertTrue($out !== null);
$decoded = json_decode((string)$out, true);
assertSame('Meine Tools', $decoded['groups'][0]['label']);
assertSame(['tickets', 'vault'], $decoded['groups'][0]['items']);
assertSame('', $decoded['groups'][1]['label']);
assertSame(['topics'], $decoded['groups'][1]['items']);
});
test('Doppelte Menüpunkte werden nur einmal übernommen', function () use ($normalize) {
$out = $normalize('{"groups":[{"label":"A","items":["tickets"]},{"label":"B","items":["tickets","ipam"]}]}');
$decoded = json_decode((string)$out, true);
assertSame(['tickets'], $decoded['groups'][0]['items']);
assertSame(['ipam'], $decoded['groups'][1]['items']);
});
test('Ungültige Strukturen werden abgewiesen', function () use ($normalize) {
assertTrue($normalize('kein json') === null);
assertTrue($normalize('{"foo":1}') === null);
assertTrue($normalize('{"groups":"x"}') === null);
assertTrue($normalize('{"groups":[1,2]}') === null);
assertTrue($normalize(str_repeat('a', 9000)) === null);
// mehr als 20 Gruppen
$many = ['groups' => array_fill(0, 21, ['label' => 'x', 'items' => []])];
assertTrue($normalize(json_encode($many)) === null);
});
test('Labels werden gekürzt, unbekannte Item-Formen verworfen', function () use ($normalize) {
$out = $normalize(json_encode(['groups' => [[
'label' => str_repeat('L', 60),
'items' => ['tickets', 'BÖSE KEY', '<script>', 123, 'ipam'],
]]]));
$decoded = json_decode((string)$out, true);
assertSame(40, mb_strlen($decoded['groups'][0]['label']));
assertSame(['tickets', 'ipam'], $decoded['groups'][0]['items']);
});
test('Speicher-Endpunkt erlaubt nav_layout und validiert', function () {
$src = file_get_contents(dirname(__DIR__) . '/src/Controllers/ApiController.php');
assertTrue(str_contains($src, "'nav_layout'"));
assertTrue(str_contains($src, 'normalizeNavLayout'));
});
test('Render-Datenfluss und Layout kennen nav_layout', function () {
$root = dirname(__DIR__);
assertTrue(str_contains(file_get_contents($root . '/src/Controllers/BaseController.php'), "\$data['nav_layout']"));
assertTrue(str_contains(file_get_contents($root . '/src/Core/Application.php'), "\$data['nav_layout']"));
$layout = file_get_contents($root . '/templates/layouts/main.php');
assertTrue(str_contains($layout, '$navCustomGroups'));
assertTrue(str_contains($layout, 'navCustomToggle'));
assertTrue(str_contains($layout, "postSetting('nav_layout'"));
// Dashboard bleibt außerhalb der konfigurierbaren Bereiche fest verankert
assertTrue(str_contains($layout, '<!-- Dashboard -->'));
});
};