From 905ce677423caf68c393c4841ef8c595e48fbcbc Mon Sep 17 00:00:00 2001 From: blaerf Date: Fri, 28 Nov 2025 15:23:25 +0100 Subject: [PATCH] Layout modularisiert --- app/Controllers/AuthController.php | 109 +-- app/Controllers/DashboardController.php | 81 +- app/Controllers/UserManagementController.php | 101 +-- public/index.php | 94 +- public/views/dashboard.php | 850 ++----------------- public/views/layout.php | 43 + public/views/layout/footer.php | 1 - public/views/layout/head.php | 22 - public/views/layout/layout.php | 1 - public/views/layout/sidebar.php | 137 --- public/views/layout/topbar.php | 1 - public/views/login.php | 147 ++-- public/views/partials/footer.php | 24 + public/views/partials/head.php | 23 + public/views/partials/scripts.php | 30 + public/views/partials/sidebar.php | 49 ++ public/views/partials/topbar.php | 49 ++ public/views/users.php | 590 ++----------- 18 files changed, 580 insertions(+), 1772 deletions(-) create mode 100644 public/views/layout.php delete mode 100644 public/views/layout/footer.php delete mode 100644 public/views/layout/head.php delete mode 100644 public/views/layout/layout.php delete mode 100644 public/views/layout/sidebar.php delete mode 100644 public/views/layout/topbar.php create mode 100644 public/views/partials/footer.php create mode 100644 public/views/partials/head.php create mode 100644 public/views/partials/scripts.php create mode 100644 public/views/partials/sidebar.php create mode 100644 public/views/partials/topbar.php diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index 0785f4d..14f9e23 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -12,6 +12,11 @@ use App\Services\Ldap\LdapAuthService; * - Login-Formular anzeigen * - Login-Daten verarbeiten (Authentifizierung gegen LDAP/AD) * - Logout durchführen + * + * NEU: + * - Statt direkt HTML auszugeben oder header()-Redirects zu setzen, + * liefert der Controller "View-Results" zurück, die von index.php + * und einem zentralen Layout verarbeitet werden. */ class AuthController { @@ -39,33 +44,36 @@ class AuthController /** * Zeigt das Login-Formular an. * Optional kann eine Fehlermeldung übergeben werden, die in der View dargestellt wird. + * + * @return array View-Result für das zentrale Layout */ - public function showLoginForm(?string $errorMessage = null): void + public function showLoginForm(?string $errorMessage = null): array { // Pfad zur Login-View (Template-Datei) ermitteln. $viewPath = __DIR__ . '/../../public/views/login.php'; - // Variable für die View vorbereiten (wird in der eingebundenen Datei verwendet). - $error = $errorMessage; - - // Falls die View-Datei (noch) nicht existiert, einen Fallback-HTML-Output verwenden. - if (file_exists($viewPath) === false) { - $this->renderInlineLogin($error); - return; - } - - // View-Datei einbinden. Variablen aus dieser Methode (z. B. $error) sind dort verfügbar. - require $viewPath; + // Wichtig: Die View erwartet aktuell die Variable $error. + return [ + 'view' => $viewPath, + 'data' => [ + 'error' => $errorMessage, + ], + 'pageTitle' => 'Login', + // Beim Login ist typischerweise kein Menüpunkt aktiv. + 'activeMenu' => null, + ]; } /** * Verarbeitet das Login-Formular: * - Liest Benutzername und Passwort aus $_POST * - Ruft den LdapAuthService zur Authentifizierung auf - * - Setzt bei Erfolg die Session und leitet zum Dashboard weiter - * - Zeigt bei Fehlern erneut das Login-Formular mit Fehlermeldung an + * - Liefert bei Erfolg ein Redirect-Result zum Dashboard + * - Liefert bei Fehlern ein View-Result für das Login-Formular mit Fehlermeldung + * + * @return array View-Result ODER Redirect-Result */ - public function processLogin(): void + public function processLogin(): array { // Formulardaten aus dem POST-Request lesen. $username = trim($_POST['username'] ?? ''); @@ -79,14 +87,14 @@ class AuthController } catch (\Throwable $exception) { // Technischer Fehler (z. B. LDAP-Server nicht erreichbar, falsche Konfiguration). // In diesem Fall wird eine technische Fehlermeldung im Login-Formular angezeigt. - $this->showLoginForm('Technischer Fehler bei der Anmeldung: ' . $exception->getMessage()); - return; + return $this->showLoginForm( + 'Technischer Fehler bei der Anmeldung: ' . $exception->getMessage() + ); } // Fachlich fehlgeschlagene Anmeldung (z. B. falsches Passwort). if ($authenticated === false) { - $this->showLoginForm('Benutzername oder Passwort ist ungültig.'); - return; + return $this->showLoginForm('Benutzername oder Passwort ist ungültig.'); } // Ab hier ist die Anmeldung erfolgreich. @@ -96,22 +104,27 @@ class AuthController $sessionKey = $this->config['security']['session_key_user'] ?? 'admin_user'; // Benutzerinformationen in der Session hinterlegen. - // Diese Daten werden später von requireLogin() ausgewertet. + // Diese Daten werden später von requireLogin() bzw. im Layout ausgewertet. $_SESSION[$sessionKey] = [ 'username' => $username, 'login_at' => date('c'), // ISO-8601 Datum/Zeit der Anmeldung ]; // Nach erfolgreicher Anmeldung zum Dashboard weiterleiten. - header('Location: index.php?route=dashboard'); - exit; + // Kein direkter header()-Aufruf, sondern ein Redirect-Result + // für die zentrale Steuerung in index.php. + return [ + 'redirect' => 'index.php?route=dashboard', + ]; } /** * Meldet den aktuell eingeloggten Benutzer ab, indem der entsprechende Session-Eintrag entfernt wird, - * und leitet anschließend zurück auf die Login-Seite. + * und liefert anschließend ein Redirect-Result zurück auf die Login-Seite. + * + * @return array Redirect-Result */ - public function logout(): void + public function logout(): array { // Session-Key für den eingeloggten Benutzer aus der Konfiguration lesen. $sessionKey = $this->config['security']['session_key_user'] ?? 'admin_user'; @@ -119,49 +132,9 @@ class AuthController // Eintrag aus der Session entfernen → Benutzer gilt als ausgeloggt. unset($_SESSION[$sessionKey]); - // Zur Login-Seite zurückleiten. - header('Location: index.php?route=login'); - exit; - } - - /** - * Fallback-Ausgabe für das Login-Formular, falls noch keine separate View-Datei existiert. - * Gibt direkt HTML aus (inline-Template). - */ - private function renderInlineLogin(?string $errorMessage): void - { - ?> - - - - - AD Admin Tool – Login - - -

AD Admin Tool – Login

- - - -

- - - -
-
- - -
- -
- - -
- - -
- - - - 'index.php?route=login', + ]; } } diff --git a/app/Controllers/DashboardController.php b/app/Controllers/DashboardController.php index 089281e..49775ba 100644 --- a/app/Controllers/DashboardController.php +++ b/app/Controllers/DashboardController.php @@ -12,6 +12,9 @@ use App\Services\Snmp\SnmpServerStatusService; * Zuständig für: * - Abrufen des Serverstatus (über SnmpServerStatusService) * - Auswählen und Rendern der Dashboard-View + * + * NEU: + * - Gibt ein View-Result zurück, das von index.php + Layout gerendert wird. */ class DashboardController { @@ -42,8 +45,10 @@ class DashboardController /** * Zeigt das Dashboard an. * Holt die Serverstatus-Daten aus dem SnmpServerStatusService und übergibt sie an die View. + * + * @return array View-Result für das zentrale Layout */ - public function show(): void + public function show(): array { // Serverstatus über den SNMP-Service ermitteln. // In der aktuellen Version liefert der Service noch Demo-Daten. @@ -52,69 +57,15 @@ class DashboardController // Pfad zur Dashboard-View (Template-Datei) ermitteln. $viewPath = __DIR__ . '/../../public/views/dashboard.php'; - // Falls die View-Datei (noch) nicht existiert, Fallback-HTML direkt aus dem Controller ausgeben. - if (file_exists($viewPath) === false) { - $this->renderInlineDashboard($serverStatus); - return; - } - - // View-Datei einbinden. Die Variable $serverStatus steht in der View zur Verfügung. - require $viewPath; - } - - /** - * Fallback-Dashboard-Ausgabe direkt aus dem Controller (ohne separate View-Datei). - * Nur als Notlösung gedacht, falls die eigentliche View noch nicht vorhanden ist. - * - * @param array $serverStatus Serverstatus-Daten (Hostname, CPU%, RAM%, etc.) - */ - private function renderInlineDashboard(array $serverStatus): void - { - ?> - - - - - AD Admin Tool – Dashboard - - -

Dashboard

- -

Serverstatus

-
    -
  • - Hostname: - -
  • -
  • - Betriebssystem: - -
  • -
  • - Uptime: - -
  • -
  • - CPU-Auslastung: - % -
  • -
  • - RAM-Auslastung: - % -
  • -
  • - Datenträger C: - % -
  • -
  • - Letzte Aktualisierung: - -
  • -
- -

Logout

- - - $viewPath, + 'data' => [ + // Die View erwartet aktuell $serverStatus. + 'serverStatus' => $serverStatus, + ], + 'pageTitle' => 'Dashboard', + // In der Sidebar soll der Dashboard-Menüpunkt aktiv sein. + 'activeMenu' => 'dashboard', + ]; } } diff --git a/app/Controllers/UserManagementController.php b/app/Controllers/UserManagementController.php index 8b20a81..67d4e4f 100644 --- a/app/Controllers/UserManagementController.php +++ b/app/Controllers/UserManagementController.php @@ -18,6 +18,9 @@ use App\Services\Ldap\LdapDirectoryService; * WICHTIG: * - Es werden aktuell nur Daten angezeigt (Read-only). * - Es findet keine Änderung im Active Directory statt. + * + * NEU: + * - Gibt ein View-Result-Array zurück, das von index.php + Layout gerendert wird. */ class UserManagementController { @@ -45,8 +48,10 @@ class UserManagementController /** * Zeigt Benutzer- und Gruppenliste an. * Wird typischerweise über die Route "users" (index.php?route=users) aufgerufen. + * + * @return array View-Result für das zentrale Layout */ - public function show(): void + public function show(): array { // Standardwerte für die View-Variablen vorbereiten. $error = null; @@ -66,88 +71,16 @@ class UserManagementController // Pfad zur eigentlichen View-Datei bestimmen. $viewPath = __DIR__ . '/../../public/views/users.php'; - // Falls die View-Datei (noch) nicht existiert, Fallback-Ausgabe verwenden. - if (file_exists($viewPath) === false) { - $this->renderInline($users, $groups, $error); - return; - } - - // Variablen $users, $groups, $error stehen in der View zur Verfügung, - // weil sie im aktuellen Scope definiert sind. - require $viewPath; - } - - /** - * Fallback-Ausgabe, falls noch keine View-Datei existiert. - * - * @param array> $users Liste der Benutzer-Datensätze - * @param array> $groups Liste der Gruppen-Datensätze - * @param string|null $error Fehlermeldung (falls vorhanden) - */ - private function renderInline(array $users, array $groups, ?string $error): void - { - ?> - - - - - AD Admin Tool – Benutzer & Gruppen - - -

Benutzer & Gruppen

- - - -

- - -

Benutzer

- - - - - - - - - - - - - - - - - -
Benutzername (sAMAccountName)AnzeigenameE-Mail
- -

Gruppen

- - - - - - - - - - - - - - - - - -
Gruppenname (sAMAccountName)CNBeschreibung
- -

- Zurück zum Dashboard | - Logout -

- - - - $viewPath, + 'data' => [ + // Die View erwartet aktuell $users, $groups, $error. + 'users' => $users, + 'groups' => $groups, + 'error' => $error, + ], + 'pageTitle' => 'Benutzer & Gruppen', + 'activeMenu' => 'users', + ]; } } diff --git a/public/index.php b/public/index.php index f84856c..a74c723 100644 --- a/public/index.php +++ b/public/index.php @@ -13,9 +13,8 @@ session_start(); * im Namespace "App\..." verwendet wird. */ spl_autoload_register( - // Anonyme Funktion, die den Klassennamen prüft und den Dateipfad zur Klasse ermittelt. static function (string $class): void { - $prefix = 'App\\'; + $prefix = 'App\\'; $baseDir = __DIR__ . '/../app/'; $len = strlen($prefix); @@ -24,7 +23,7 @@ spl_autoload_register( } $relativeClass = substr($class, $len); - $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; + $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php'; if (file_exists($file) === true) { require $file; @@ -32,6 +31,8 @@ spl_autoload_register( } ); +require __DIR__ . '/views/layout.php'; + // Pfad zur Konfigurationsdatei prüfen und Konfiguration laden $configPath = __DIR__ . '/../config/config.php'; if (file_exists($configPath) === false) { @@ -49,7 +50,12 @@ use App\Controllers\AuthController; use App\Controllers\DashboardController; use App\Controllers\UserManagementController; -// Hilfsfunktion für geschützte Routen +/** + * Hilfsfunktion: Prüft, ob ein Benutzer eingeloggt ist. + * Wenn nicht, wird auf die Login-Seite umgeleitet. + * + * @param array $config + */ function requireLogin(array $config): void { // session_key_user aus dem Config-Array lesen. Wenn nicht gesetzt oder null, Standard "admin_user" verwenden. @@ -63,44 +69,93 @@ function requireLogin(array $config): void } } -// Route aus dem GET-Parameter lesen. Wenn nicht gesetzt, Standardroute "login" verwenden. -$route = $_GET['route'] ?? 'login'; +/** + * Hilfsfunktion: Ergebnis-Array eines Controllers nehmen und das Layout rendern. + * + * Erwartetes Format im $result-Array: + * [ + * 'view' => string, // Pfad zur Content-View (z. B. public/views/dashboard.php) + * 'data' => array // Daten für die View + * 'pageTitle' => string, // Seitentitel + * 'activeMenu' => string|null, // Menükennung für Sidebar (z. B. 'dashboard' oder 'users') + * ] + * + * @param array $result + */ +function handleResult(?array $result): void +{ + if ($result === null) { + return; + } -// Neue Instanz der Klasse AuthController erstellen (wird bei Bedarf über den Autoloader geladen). + if (isset($result['redirect']) === true) { + header('Location: ' . (string)$result['redirect']); + exit; + } + + $contentView = (string)($result['view'] ?? ''); + $viewData = (array)($result['data'] ?? []); + $pageTitle = (string)($result['pageTitle'] ?? ''); + $activeMenu = $result['activeMenu'] ?? null; + + if ($contentView === '' || file_exists($contentView) === false) { + http_response_code(500); + echo 'Interner Fehler: Content-View wurde nicht gefunden.'; + exit; + } + + // Hier rufen wir jetzt die Layout-Funktion aus layout.php auf + renderLayout($contentView, $viewData, $pageTitle, is_string($activeMenu) ? $activeMenu : null); +} + +// Controller instanziieren $authController = new AuthController($config); - -// Neue Instanz der Klasse DashboardController erstellen (wird bei Bedarf über den Autoloader geladen). $dashboardController = new DashboardController($config); - -// Neue Instanz der Klasse UserManagmentController erstellen (wird bei Bedarf über den Autoloader geladen). $userManagementController = new UserManagementController($config); -// Anhand des Routing-Ziels (route) entscheiden, welcher Code ausgeführt wird. +// Route bestimmen +$route = $_GET['route'] ?? 'login'; + switch ($route) { case 'login': - $authController->showLoginForm(); + // Wenn bereits eingeloggt, direkt zum Dashboard + $sessionKey = $config['security']['session_key_user'] ?? 'admin_user'; + if (isset($_SESSION[$sessionKey]) === true) { + header('Location: index.php?route=dashboard'); + exit; + } + + // Controller liefert View-Definition + Daten + $result = $authController->showLoginForm(); + renderLayout($result); break; case 'login.submit': - if ($_SERVER['REQUEST_METHOD'] !== 'POST') { - header('Location: index.php?route=login'); - exit; + // POST-Login wird im Controller verarbeitet. + // Der Controller entscheidet, ob er redirectet oder ein View-Ergebnis zurückgibt. + $result = $authController->processLogin(); + if ($result !== null) { + // z. B. bei Login-Fehler: Login-Seite mit Fehlermeldung anzeigen + renderLayout($result); } - $authController->processLogin(); + // Bei erfolgreichem Login macht der Controller einen Redirect und beendet das Script. break; case 'logout': $authController->logout(); + // logout() sollte selbst redirecten (z. B. auf route=login) break; case 'dashboard': requireLogin($config); - $dashboardController->show(); + $result = $dashboardController->show(); + renderLayout($result); break; case 'users': requireLogin($config); - $userManagementController->show(); + $result = $userManagementController->show(); + renderLayout($result); break; default: @@ -108,3 +163,4 @@ switch ($route) { echo 'Route nicht gefunden.'; break; } + diff --git a/public/views/dashboard.php b/public/views/dashboard.php index 3164c36..0fe1dad 100644 --- a/public/views/dashboard.php +++ b/public/views/dashboard.php @@ -1,792 +1,100 @@ $serverStatus */ ?> - - - +
+

Server-Dashboard

+
- - - - - +
- SB Admin 2 - Dashboard - - - - - - - - - - - - - -
- - - - - - -
- - -
- - - - - - -
- - -
-

Dashboard

- Generate Report -
- - -
- - -
-
-
-
-
-
CPU -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
RAM -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
HDD (C:) -
-
-
-
%
-
-
-
-
aria-valuenow= aria-valuemin="0" - aria-valuemax="100">
-
-
-
-
-
- -
-
-
-
-
- - -
-
-
-
-
-
- Pending Requests
-
18
-
-
- -
-
-
-
+
+
- - - -
- - -
-
- -
-
Earnings Overview
- -
- -
-
- -
-
-
-
- - -
-
- -
-
Revenue Sources
- -
- -
-
- -
-
- - Direct - - - Social - - - Referral - -
-
-
-
+
+
- - -
- - -
- - -
-
-
Projects
-
-
-

Server Migration 20%

-
-
-
-

Sales Tracking 40%

-
-
-
-

Customer Database 60%

-
-
-
-

Payout Details 80%

-
-
-
-

Account Setup Complete!

-
-
-
-
-
- - -
-
-
-
- Primary -
#4e73df
-
-
-
-
-
-
- Success -
#1cc88a
-
-
-
-
-
-
- Info -
#36b9cc
-
-
-
-
-
-
- Warning -
#f6c23e
-
-
-
-
-
-
- Danger -
#e74a3b
-
-
-
-
-
-
- Secondary -
#858796
-
-
-
-
-
-
- Light -
#f8f9fc
-
-
-
-
-
-
- Dark -
#5a5c69
-
-
-
-
- -
- -
- - -
-
-
Illustrations
-
-
-
- ... -
-

Add some quality, svg illustrations to your project courtesy of unDraw, a - constantly updated collection of beautiful svg images that you can use - completely free and without attribution!

- Browse Illustrations on - unDraw → -
-
- - -
-
-
Development Approach
-
-
-

SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce - CSS bloat and poor page performance. Custom CSS classes are used to create - custom components and custom utility classes.

-

Before working with this theme, you should become familiar with the - Bootstrap framework, especially the utility classes.

-
-
- -
-
- -
- - -
- - - -
-
- -
-
- - -
- - -
- - - - - - - - - - - - + +
+
+
+
+
+
+ Betriebssystem +
+
+ +
+
+
+ +
+
+
+
+
- - + +
+
+
+
+
+
+ CPU-Auslastung +
+
+ % +
+
+
+ +
+
+
+
+
- - + +
+
+
+
+
+
+ RAM-Auslastung +
+
+ % +
+
+
+ +
+
+
+
+
- - +
- - - - - - - \ No newline at end of file + diff --git a/public/views/layout.php b/public/views/layout.php new file mode 100644 index 0000000..b7be8f2 --- /dev/null +++ b/public/views/layout.php @@ -0,0 +1,43 @@ + $viewData Daten-Array für die View + * - string $pageTitle Seitentitel + * - string|null $activeMenu Kennung für die Sidebar (z. B. 'dashboard' oder 'users') + */ + +// Daten-Array in einzelne Variablen entpacken, +// sodass die Content-Views direkt mit $users, $groups, $serverStatus, $error etc. arbeiten können. +function renderLayout(string $contentView, array $viewData, string $pageTitle, ?string $activeMenu): void +{ + // Daten-Array in einzelne Variablen entpacken + foreach ($viewData as $key => $value) { + if (is_string($key) && $key !== '') { + $cleanKey = preg_replace('/[^a-zA-Z0-9_]/', '', $key); + if ($cleanKey !== '') { + $$cleanKey = $value; + } + } + } + + $partialsPath = __DIR__ . '/partials'; + + require $partialsPath . '/head.php'; + require $partialsPath . '/sidebar.php'; + require $partialsPath . '/topbar.php'; + ?> + +
+ +
+ + - - - - - - -<?php echo htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?> - - - - - - - diff --git a/public/views/layout/layout.php b/public/views/layout/layout.php deleted file mode 100644 index b3d9bbc..0000000 --- a/public/views/layout/layout.php +++ /dev/null @@ -1 +0,0 @@ - - - - - diff --git a/public/views/layout/topbar.php b/public/views/layout/topbar.php deleted file mode 100644 index b3d9bbc..0000000 --- a/public/views/layout/topbar.php +++ /dev/null @@ -1 +0,0 @@ - - - +
- - - - - - - - - SB Admin 2 - Login - - - - - - - - - - - - -
- - -
- -
- -
-
- -
- -
-
-
-

Welcome Back!

-
- - - -
-
- -
-
- -
-
-
- - -
-
- -
-
- - -
+
+
+
+ +
+
+
+
+

Willkommen beim AD Admin Tool

+

+ Anmeldung gegen Active Directory per LDAP/LDAPS. +

+ + + + + +
+
+ + +
+
+ + +
+ + +
+ +
+

+ Die Zugangsdaten werden nicht gespeichert – sie werden nur zur Anmeldung am AD verwendet. +

-
-
-
- - - - - - - - - - - - - \ No newline at end of file +
diff --git a/public/views/partials/footer.php b/public/views/partials/footer.php new file mode 100644 index 0000000..23aee7b --- /dev/null +++ b/public/views/partials/footer.php @@ -0,0 +1,24 @@ + +
+ + +
+ + + +
+
+ +
+
+ + +
+ + +
+ diff --git a/public/views/partials/head.php b/public/views/partials/head.php new file mode 100644 index 0000000..1f5ce7b --- /dev/null +++ b/public/views/partials/head.php @@ -0,0 +1,23 @@ + + + + + + + AD Admin Tool<?= isset($pageTitle) ? ' – ' . htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') : '' ?> + + + + + + + + + + + + + +
diff --git a/public/views/partials/scripts.php b/public/views/partials/scripts.php new file mode 100644 index 0000000..5845a34 --- /dev/null +++ b/public/views/partials/scripts.php @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/views/partials/sidebar.php b/public/views/partials/sidebar.php new file mode 100644 index 0000000..9a4047e --- /dev/null +++ b/public/views/partials/sidebar.php @@ -0,0 +1,49 @@ + + + + diff --git a/public/views/partials/topbar.php b/public/views/partials/topbar.php new file mode 100644 index 0000000..d29658c --- /dev/null +++ b/public/views/partials/topbar.php @@ -0,0 +1,49 @@ + + +
+ + +
+ + + + + + +
diff --git a/public/views/users.php b/public/views/users.php index ebd600a..0ef3b5f 100644 --- a/public/views/users.php +++ b/public/views/users.php @@ -1,520 +1,94 @@ > $users */ -/** @var array> $groups */ -/** @var string|null $error */ +/** + * @var array> $users + * @var array> $groups + * @var string|null $error + */ ?> - - - - - - - - - - - - SB Admin 2 - Tables - - - - - - - - - - - - - - - - -
- - - - - - -
- - -
- - - - - - -
- - -

Benutzer & Gruppen

- -

- -

- -

DataTables is a third party plugin that is used to generate the demo table below. - For more information about DataTables, please visit the official DataTables documentation.

- - -
-
-
Benutzer
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
AnmeldenameAnzeigenameE-Mail
AnmeldenameAnzeigenameE-Mail
-
-
-
-
-
-
Gruppen
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
GruppennameCNBeschreibung
GruppennameCNBeschreibung
-
-
-
- -
- - -
- - - -
-
- -
-
- - -
- +
+

Benutzer & Gruppen

- - - - - + + + - -