fail("adminFieldsFile fehlt: $relPath"); return; } $data = json_decode(file_get_contents($path), true); if (!is_array($data)) { $r->fail("$relPath ist kein valides JSON"); return; } // moduleId muss matchen if (!isset($data['moduleId'])) { $r->fail("$relPath: 'moduleId' fehlt"); } elseif ($data['moduleId'] !== ($m['slug'] ?? null)) { $r->fail("$relPath: 'moduleId' = '{$data['moduleId']}' stimmt nicht mit manifest.slug = '{$m['slug']}' überein"); } if (!isset($data['groups'])) { $r->fail("$relPath: 'groups' fehlt (kann leeres Array sein)"); return; } if (!is_array($data['groups'])) { $r->fail("$relPath: 'groups' muss ein Array sein"); return; } if (count($data['groups']) === 0) { $r->ok("admin-fields.json: leer (Modul ohne Tuning-Parameter — ok)"); return; } // Pro Gruppe: title + fields $totalFields = 0; foreach ($data['groups'] as $i => $grp) { if (!isset($grp['title'])) { $r->fail("$relPath: groups[$i].title fehlt"); } if (!isset($grp['fields']) || !is_array($grp['fields'])) { $r->fail("$relPath: groups[$i].fields fehlt oder kein Array"); continue; } foreach ($grp['fields'] as $j => $field) { if (!isset($field['key'], $field['label'], $field['labelEasy'], $field['type'])) { $r->fail("$relPath: groups[$i].fields[$j] braucht key, label, labelEasy, type"); } $totalFields++; } } $r->ok("admin-fields.json: " . count($data['groups']) . " Gruppen, $totalFields Felder gesamt"); }