v2.5.1: Admin theme refactor, Navigation plugin, user roles, guide restructure

- Reorganize admin into admin/theme/default/ (views + assets)
- Rename GuideNav to Navigation plugin (essential, protected)
- Plugin assets support (SCSS/CSS) loaded after theme CSS
- User roles: Admin, Content Manager, BI Manager, Site Admin
- Role-based access control (RBAC) for admin routes and sidebar
- Guide restructure: sub-topics in separate folders with sidebar nav
- Dynamic breadcrumb for homepage and subdirectories
- Fix theme path traversal (../../ -> ../) in admin.php
- Fix CodeMirror mode load order (xml -> css -> js -> htmlmixed -> php)
- Fix editor-toolbar.js null checks for plugin edit pages
- Layout select from theme.json with live frontmatter update
- Footer sticky at bottom of viewport (min-height: 100vh)
- Breadcrumb color fix (var(--nav-font) -> var(--header-bg))
- Remove language switcher from guide pages
- Update README.md and README.en.md
- Bump version to 2.5.1
This commit is contained in:
2026-08-10 15:36:29 +02:00
parent 7fc3847bbb
commit 3a55ea4db6
210 changed files with 11033 additions and 9140 deletions
+92 -5
View File
@@ -9,6 +9,28 @@ class AdminAuth
private array $adminConfig;
private string $lockFile;
/**
* Role definitions with permissions.
* Each role maps to a list of allowed route prefixes.
* 'admin' has wildcard '*' access.
*/
public const ROLE_PERMISSIONS = [
'admin' => ['*'],
'content-manager' => ['dashboard', 'content', 'content-edit', 'content-new', 'content-delete', 'content-dir-create', 'content-dir-rename', 'content-dir-delete', 'content-move', 'guide', 'logout'],
'bi-manager' => ['dashboard', 'statistics', 'logs', 'guide', 'logout'],
'site-admin' => ['dashboard', 'theme', 'theme-new', 'plugins', 'plugins-new', 'plugins-edit', 'plugins-config', 'plugins-toggle', 'plugins-delete', 'statistics', 'logs', 'update', 'guide', 'logout'],
];
/**
* Human-readable role labels.
*/
public const ROLE_LABELS = [
'admin' => 'Admin',
'content-manager' => 'Content Beheerder',
'bi-manager' => 'BI Beheerder',
'site-admin' => ' Site Admin',
];
public function __construct(array $appConfig)
{
$this->config = $appConfig;
@@ -154,6 +176,45 @@ class AdminAuth
];
}
/**
* Get the role of the current user.
*/
public function getCurrentRole(): string
{
return $_SESSION['admin_role'] ?? 'admin';
}
/**
* Check if the current user has permission to access a route.
*/
public function hasPermission(string $route): bool
{
$role = $this->getCurrentRole();
$permissions = self::ROLE_PERMISSIONS[$role] ?? ['dashboard', 'logout'];
if (in_array('*', $permissions, true)) {
return true;
}
return in_array($route, $permissions, true);
}
/**
* Get available roles.
*/
public static function getRoles(): array
{
return self::ROLE_LABELS;
}
/**
* Get role label.
*/
public static function getRoleLabel(string $role): string
{
return self::ROLE_LABELS[$role] ?? $role;
}
public function getCsrfToken(): string
{
if (!isset($_SESSION['admin_csrf_token'])) {
@@ -176,13 +237,17 @@ class AdminAuth
public function getUsers(): array
{
return array_map(function ($u) {
return [
$users = [];
foreach ($this->adminConfig['users'] ?? [] as $u) {
$role = $u['role'] ?? 'admin';
$users[$u['username']] = [
'username' => $u['username'],
'role' => $u['role'] ?? 'admin',
'role' => $role,
'role_label' => self::getRoleLabel($role),
'created' => $u['created'] ?? ''
];
}, $this->adminConfig['users'] ?? []);
}
return $users;
}
public function addUser(string $username, string $password, string $role = 'admin'): array
@@ -193,6 +258,9 @@ class AdminAuth
if (strlen($password) < 8) {
return ['success' => false, 'message' => 'Wachtwoord moet minimaal 8 tekens zijn.'];
}
if (!isset(self::ROLE_PERMISSIONS[$role])) {
return ['success' => false, 'message' => 'Ongeldige rol.'];
}
$this->adminConfig['users'][] = [
'username' => $username,
@@ -201,10 +269,29 @@ class AdminAuth
'created' => date('Y-m-d')
];
$this->saveAdminConfig();
$this->log('info', "Gebruiker aangemaakt: {$username}");
$this->log('info', "Gebruiker aangemaakt: {$username} (rol: {$role})");
return ['success' => true, 'message' => 'Gebruiker aangemaakt.'];
}
/**
* Change the role of an existing user.
*/
public function changeRole(string $username, string $role): array
{
if (!isset(self::ROLE_PERMISSIONS[$role])) {
return ['success' => false, 'message' => 'Ongeldige rol.'];
}
foreach ($this->adminConfig['users'] as &$user) {
if ($user['username'] === $username) {
$user['role'] = $role;
$this->saveAdminConfig();
$this->log('info', "Rol gewijzigd: {$username} -> {$role}");
return ['success' => true, 'message' => 'Rol gewijzigd.'];
}
}
return ['success' => false, 'message' => 'Gebruiker niet gevonden.'];
}
public function deleteUser(string $username): array
{
if ($username === ($_SESSION['admin_user'] ?? '')) {