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
+15
View File
@@ -0,0 +1,15 @@
# Admin Manager
The admin manager guide contains the following topics:
- **Dashboard** - Website overview
- **Content management** - Managing files and pages
- **Configuration** - Site settings
- **Theme management** - Managing and creating themes
- **Security** - Bot protection and sessions
- **Plugins** - Managing and creating plugins
- **Users** - Adding and removing users
- **Statistics** - Viewing visitor statistics
- **Logs** - Viewing and filtering log files
Select a topic from the navigation on the left.
+12
View File
@@ -0,0 +1,12 @@
# Security
## Bot protection
- **BotGuard** - Bot/AI detection
- **Rate limiting** - Limit requests per IP
- **Block/allowlist** - Block or allow IPs
## Session settings
- **Session timeout** - Default 3600 seconds
- **Max login attempts** - Default 5 attempts
+9
View File
@@ -0,0 +1,9 @@
# Configuration
## General settings
- **Site title** - Website name
- **Default language** - nl/en/de/fr
- **Author** - Name and email
- **Analytics** - Visitor tracking on/off
- **Logging** - Log files on/off
@@ -0,0 +1,17 @@
# Content management
## Managing files
- **Upload** - Upload media files
- **New folder** - Create folder structure
- **New file** - Create a page
- **Edit** - Modify existing content
- **Rename** - Change file names
- **Move** - Move content
- **Delete** - Remove content
## Editor
- CodeMirror with syntax highlighting
- Toolbar for Markdown formatting
- Shortcuts: Ctrl+S (save), Ctrl+N (new)
+10
View File
@@ -0,0 +1,10 @@
# Dashboard
The dashboard shows an overview of:
- Views (30 days)
- Unique visitors
- Number of pages and folders
- Active plugins
- Recent activity
- Quick actions
+13
View File
@@ -0,0 +1,13 @@
# Users
## Add user
1. Go to **Users**
2. Enter username
3. Choose password
4. Click **Add**
## Delete user
- Cannot delete own account
- Confirm with password
+12
View File
@@ -0,0 +1,12 @@
# Logs
## Log types
- **Admin** - Admin actions
- **Requests** - Website visits
## Filter
- Search by IP, message
- Filter by level (info, warning, error)
- Download as file
+15
View File
@@ -0,0 +1,15 @@
# Plugins
## Managing plugins
- **Enable/Disable** - Turn plugins on/off
- **Edit** - Modify plugin code
- **Configuration** - Plugin settings
- **Delete** - Remove plugin
## New plugin
1. Go to **Plugins****New plugin**
2. Enter a name (e.g. `MyPlugin`)
3. Edit `plugin.php`
4. Enable the plugin
+14
View File
@@ -0,0 +1,14 @@
# Statistics
## Overview
- Total views
- Unique visitors
- Countries with flags
- Top pages
- Referrers
## Filters
- Period: 7/30/90 days or all
- Export: CSV or JSON
+21
View File
@@ -0,0 +1,21 @@
# Theme management
## Managing themes
1. Go to **Theme** in admin menu
2. **Activate** - Choose active theme
3. **Compile SCSS** - Process SCSS to CSS
4. **New theme** - Create custom theme
## Theme structure
```
themes/default/
├── theme.json # Theme configuration
├── base.twig # Main layout
├── full_content.twig # Layouts
├── left_sidebar.twig
├── right_sidebar.twig
├── partials/ # Header, nav, footer
└── assets/ # CSS, JS, images
```
+14
View File
@@ -0,0 +1,14 @@
# CodePress Developer
The CodePress developer guide contains the following topics:
- **Architecture** - CMS architecture and folder structure
- **Core classes** - CodePressCMS, ThemeManager, PluginManager
- **Plugin development** - Developing plugins
- **Routing** - Frontend and admin routing
- **Security** - XSS, CSRF, path traversal
- **Testing** - Penetration, accessibility and functional tests
- **Debugging** - Logging and cache
- **Performance** - OPcache and SCSS caching
Select a topic from the navigation on the left.
@@ -0,0 +1,11 @@
# CMS Architecture
```
codepress/
├── cms/core/ # Core engine
├── admin/ # Admin console
├── themes/ # Themes
├── plugins/ # Plugins
├── content/ # Content
└── public/ # Web root
```
@@ -0,0 +1,34 @@
# Security
## XSS prevention
```php
// Always escape
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// In Twig (automatic)
{{ userVariable }}
```
## CSRF tokens
```php
// Generate
$csrf = $auth->getCsrfToken();
// Verify
if (!$auth->verifyCsrf($_POST['csrf_token'])) {
die('Invalid CSRF token');
}
```
## Path traversal prevention
```php
// Use realpath() and check prefix
$realPath = realpath($filePath);
$realContentDir = realpath($contentDir);
if (strpos($realPath, $realContentDir) !== 0) {
die('Invalid path');
}
```
@@ -0,0 +1,32 @@
# Core Classes
## CodePressCMS.php
Main CMS class in `cms/core/class/CodePressCMS.php`:
```php
$cms = new CodePressCMS();
$cms->init();
$cms->renderPage($pagePath);
```
## ThemeManager.php
Theme management in `cms/core/class/ThemeManager.php`:
```php
$themeManager = new ThemeManager($config);
$themeManager->getActiveTheme();
$themeManager->renderTwig($template, $data);
$themeManager->compileCss($force);
```
## PluginManager.php
Plugin system in `cms/core/class/PluginManager.php`:
```php
$pluginManager = new PluginManager();
$pluginManager->loadPlugins($enabledPlugins);
$pluginManager->executeHook($name, $params);
```
+23
View File
@@ -0,0 +1,23 @@
# Debugging
## Logging
```php
// Admin logging
adminLog($config, 'info', 'Message text');
// LogManager
LogManager::log(LogManager::EVENT_ADMIN, 'info', 'Message');
```
## Disabling cache
In `config.json`:
```json
{
"cache": {
"enabled": false
}
}
```
@@ -0,0 +1,19 @@
# Performance
## Enabling OPcache
In `php.ini`:
```ini
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
```
## SCSS caching
SCSS is compiled with caching:
```php
$themeManager->compileCss(false); // Use cache when possible
```
@@ -0,0 +1,43 @@
# Plugin Development
## Plugin structure
```
plugins/MyPlugin/
├── plugin.json # Plugin metadata
├── plugin.php # Plugin code
└── config.json # Optional configuration
```
## plugin.json
```json
{
"name": "My Plugin",
"version": "1.0.0",
"author": "Your Name",
"description": "Description"
}
```
## plugin.php example
```php
<?php
/**
* Plugin: MyPlugin
*/
echo '<div class="my-plugin">Hello World</div>';
```
## Using CMSAPI
```php
<?php
require_once '../../cms/core/class/PluginManager.php';
$api = PluginManager::getAPI();
$config = $api->getConfig();
$content = $api->getContent();
```
+19
View File
@@ -0,0 +1,19 @@
# Routing
## Frontend routing
Via `cms/router.php` for PHP dev server:
```php
// Clean URLs: /nl/page
// Query: ?page=page&lang=nl
```
## Admin routing
Via `public/admin.php`:
```php
// Routes: /admin/dashboard, /admin/content, etc.
// Query parameter: ?route=dashboard
```
+22
View File
@@ -0,0 +1,22 @@
# Testing
## Penetration tests
```bash
cd cli/test/pentest
./security-test.sh
```
## Accessibility tests
```bash
cd cli/test
./accessibility.sh
```
## Functional tests
```bash
cd cli/test/functional
./content-tests.sh
```
+10
View File
@@ -0,0 +1,10 @@
# Content Manager
The content manager guide contains the following topics:
- **Content structure** - How content is stored
- **Managing pages** - Creating and editing pages
- **Managing media** - Uploading and using media
- **Content API** - Using the Content API in PHP pages
Select a topic from the navigation on the left.
+119
View File
@@ -0,0 +1,119 @@
# Content API
The Content API is available in PHP content files (`.php`) and provides a safe, read-only interface to CMS data.
## Usage in PHP content files
```php
<?php
/** @var ContentAPI $api */
// Get all pages
$allPages = $api->getAllPages();
// Result: ['index' => 'Home', 'about-us' => 'About Us', ...]
// Get a specific page
$page = $api->getPage('about-us');
// Result: ['title' => 'About Us', 'content' => '...', 'path' => 'about-us', 'layout' => 'full_content', 'metadata' => [...]]
// Get menu structure
$menu = $api->getMenu();
// Result: [['title' => 'Home', 'path' => 'index', 'type' => 'file', 'active' => true], ...]
// Get config value (dot notation)
$siteTitle = $api->getConfig('site_title');
$searchEnabled = $api->getConfig('features.search_enabled', false);
```
## Available methods
### Pages
- `getAllPages(): array` - All pages as `['path' => 'title']` pairs
- `getPage(string $path): ?array` - Specific page with `title`, `content`, `path`, `layout`, `metadata`
- `pageExists(string $path): bool` - Check if a page exists
- `getCurrentPageTitle(): string` - Title of current page
- `getCurrentPagePath(): string` - Path of current page
- `isHomepage(): bool` - Whether current page is the homepage
### Menu & Navigation
- `getMenu(): array` - Hierarchical menu structure with `title`, `path`, `children`, `active`
- `buildUrl(string $page = 'index', ?string $lang = null, array $params = []): string` - Build a URL for a page
### Configuration
- `getConfig(string $key, mixed $default = null): mixed` - Config value via dot notation (e.g. `'features.search_enabled'`)
- `getSiteTitle(): string` - Site title from config
### Language
- `getCurrentLanguage(): string` - Current language code (e.g. `'nl'`)
- `getAvailableLanguages(): array` - Available languages (e.g. `['nl', 'en']`)
- `t(string $key): string` - Translate a language key
### Search
- `getSearchResults(): array` - Search results (empty if not searching)
- `isSearching(): bool` - Whether a search is currently active
## Example: Show recent pages
```php
<?php
/** @var ContentAPI $api */
$pages = $api->getAllPages();
$currentLang = $api->getCurrentLanguage();
?>
<ul>
<?php foreach (array_slice($pages, 0, 5, true) as $path => $title): ?>
<li>
<a href="/<?= $currentLang ?>/<?= htmlspecialchars($path) ?>">
<?= htmlspecialchars($title) ?>
</a>
</li>
<?php endforeach; ?>
</ul>
```
## Example: Using config values
```php
<?php
/** @var ContentAPI $api */
if ($api->getConfig('features.search_enabled', false)): ?>
<form method="GET" action="">
<input type="search" name="search" placeholder="<?= $api->t('search_placeholder') ?>">
<button type="submit"><?= $api->t('search_button') ?></button>
</form>
<?php endif; ?>
```
## CMSAPI (for plugins)
Plugins use the `CMSAPI` class via `$this->api`. It offers similar methods:
```php
$this->api->getCurrentPageTitle();
$this->api->getCurrentPageContent();
$this->api->getMenu();
$this->api->getConfig('site_title');
$this->api->getCurrentLanguage();
$this->api->isHomepage();
$this->api->hasContent();
$this->api->getSearchResults();
$this->api->isSearching();
$this->api->getAvailableLanguages();
$this->api->createUrl('about-us');
$this->api->translate('home');
```
Additionally, CMSAPI provides:
- `getCurrentPage(): array` - Full page data
- `getCurrentPageUrl(): string` - URL of current page
- `getCurrentPageFileInfo(): ?array` - File info (created, modified)
- `getBreadcrumb(): string` - Breadcrumb HTML
- `executePhpFile(string $filePath): string` - Execute PHP file and capture output
- `getFileContent(string $filePath): string` - Get content from PHP/HTML/Markdown file
- `contentFileExists(string $filename): bool` - Check if file exists in content directory
@@ -0,0 +1,31 @@
# Content structure
Content is stored in the `content/` folder without a database.
## Supported file formats
- `.md` - Markdown (recommended)
- `.php` - Dynamic PHP pages
- `.html` - Static HTML pages
## File name conventions
```
en.page-name.md # English page
nl.pagina-naam.md # Dutch page
```
## Frontmatter
Markdown files can contain frontmatter metadata:
```markdown
---
layout: full_content
plugins: HTMLBlock
---
# Page title
Content...
```
@@ -0,0 +1,16 @@
# Managing media
## Uploading
1. Go to Content folder
2. Click **Upload**
3. Select files (JPG, PNG, GIF, WebP, SVG, PDF, etc.)
4. Upload
## Using in content
```markdown
![Alt text](/content/image.jpg)
<video src="/content/video.mp4" controls></video>
```
@@ -0,0 +1,16 @@
# Managing pages
## Via Admin Console
1. Login at `/admin`
2. Go to **Content**
3. Choose a folder or create a new page
4. Edit content in the editor
5. Save with Ctrl+S
## Editor features
- **CodeMirror** editor with syntax highlighting
- **Toolbar** for quick Markdown insertion
- **Live preview** via button
- **Auto-save** backups in `.bak/` folder
+3
View File
@@ -0,0 +1,3 @@
# Manual
Select a topic from the navigation on the left.
+14
View File
@@ -0,0 +1,14 @@
# Theme Developer
The theme developer guide contains the following topics:
- **Theme structure** - Folder structure of a theme
- **theme.json** - Theme configuration
- **Twig templates** - Twig syntax, variables and blocks
- **SCSS styling** - Compiling and writing SCSS
- **Layouts** - Defining and using layouts
- **New theme** - Creating a theme via admin or manually
See also the **Content API** guide under Content Manager for all available Twig variables and CMSAPI methods.
Select a topic from the navigation on the left.
+27
View File
@@ -0,0 +1,27 @@
# Layouts
## Choosing a layout in content
```markdown
---
layout: left_sidebar
---
# Page title
Content...
```
## Layouts in theme.json
```json
{
"default_layout": "full_content",
"layouts": {
"full_content": "full_content.twig",
"left_sidebar": "left_sidebar.twig",
"right_sidebar": "right_sidebar.twig",
"custom1": "custom1.twig"
}
}
```
+25
View File
@@ -0,0 +1,25 @@
# Creating a new theme
## Via Admin
1. Go to **Theme****New theme**
2. Enter a name (e.g. `my-theme`)
3. Theme is created with basic structure
4. Edit `theme.json` and templates
## Manually
```bash
mkdir themes/my-theme
mkdir themes/my-theme/partials
mkdir themes/my-theme/assets/{scss,css,js,fonts,img}
```
Create basic files:
- `theme.json`
- `base.twig`
- `full_content.twig`
- `partials/header.twig`
- `partials/footer.twig`
- `scss/theme.scss`
+24
View File
@@ -0,0 +1,24 @@
# SCSS styling
## Compiling theme.scss
1. Go to **Admin****Theme**
2. Click **Compile SCSS** for your theme
3. CSS is generated in `assets/css/theme.css`
## SCSS example
```scss
$primary-color: #0a369d;
$font-stack: Helvetica, Arial, sans-serif;
body {
font-family: $font-stack;
color: #333;
}
.header {
background: $primary-color;
color: #fff;
}
```
@@ -0,0 +1,21 @@
# Theme structure
```
themes/my-theme/
├── theme.json # Theme configuration
├── base.twig # Main layout
├── full_content.twig # Layout: full-width
├── left_sidebar.twig # Layout: left sidebar
├── right_sidebar.twig # Layout: right sidebar
├── custom.twig # Layout: custom
├── partials/
│ ├── header.twig
│ ├── navigation.twig
│ └── footer.twig
└── assets/
├── scss/theme.scss # SCSS source
├── css/ # CSS files
├── js/theme.js # JavaScript
├── fonts/ # Fonts
└── img/ # Images
```
+21
View File
@@ -0,0 +1,21 @@
# theme.json
```json
{
"title": "My Theme",
"default_layout": "full_content",
"header_color": "#0a369d",
"layouts": {
"full_content": "full_content.twig",
"left_sidebar": "left_sidebar.twig",
"right_sidebar": "right_sidebar.twig"
}
}
```
## Fields
- `title` - Display name in admin
- `default_layout` - Default layout for new pages
- `header_color` - Admin sidebar color
- `layouts` - Mapping of layout names to .twig files
+206
View File
@@ -0,0 +1,206 @@
# Twig templates
## Basic syntax
```twig
{# Comment #}
{{ variable }}
{{ variable|default('default') }}
{% if condition %}...{% endif %}
{% for item in items %}...{% endfor %}
{% extends 'base.twig' %}
{% block content %}{% endblock %}
{% include 'partials/header.twig' %}
```
## base.twig example
```twig
<!DOCTYPE html>
<html lang="{{ current_lang }}">
<head>
<meta charset="UTF-8">
<title>{{ page_title }} - {{ site_title }}</title>
<meta name="description" content="{{ seo_description }}">
<link rel="stylesheet" href="{{ theme_css_url }}">
</head>
<body>
{% include 'partials/header.twig' %}
{% block content %}{% endblock %}
{% include 'partials/footer.twig' %}
</body>
</html>
```
## Variables available in templates
### Page data
| Variable | Type | Description |
|----------|------|-------------|
| `page_title` | string | Current page title (HTML-escaped) |
| `content` | string | Rendered page content (HTML) |
| `page_metadata` | array | Frontmatter metadata of the page |
| `layout` | string | Layout name (e.g. `full_content`) |
| `sidebar_content` | string | Sidebar content from plugins (HTML) |
| `breadcrumb` | string | Breadcrumb HTML (generated by CMS) |
### Site data
| Variable | Type | Description |
|----------|------|-------------|
| `site_title` | string | Site title from config.json |
| `default_page` | string | Default page path |
| `homepage` | string | Homepage path |
| `homepage_title` | string | Homepage title |
| `is_homepage` | bool | Whether current page is the homepage |
| `is_guide_page` | bool | Whether current page is a guide page |
| `has_content` | bool | Whether content directory is not empty |
| `cms_version` | string | CMS version number |
### Menu & Navigation
| Variable | Type | Description |
|----------|------|-------------|
| `menu` | string | Rendered menu HTML |
| `current_page` | string | Current page path |
| `breadcrumb` | string | Breadcrumb HTML |
### Language
| Variable | Type | Description |
|----------|------|-------------|
| `current_lang` | string | Current language code (e.g. `nl`) |
| `current_lang_upper` | string | Current language in uppercase |
| `available_langs` | array | Available languages with `code`, `name`, `url`, `is_current` |
### SEO
| Variable | Type | Description |
|----------|------|-------------|
| `seo_description` | string | SEO description |
| `seo_keywords` | string | SEO keywords |
| `block_ai_bots` | bool | Block AI bots |
| `block_search_engines` | bool | Block search engines |
### Author
| Variable | Type | Description |
|----------|------|-------------|
| `author_name` | string | Author name |
| `author_website` | string | Author website URL |
| `author_git` | string | Author Git URL |
### Theme
| Variable | Type | Description |
|----------|------|-------------|
| `theme_title` | string | Theme title |
| `theme_css_url` | string | Compiled CSS URL |
| `theme_js_url` | string | JavaScript URL |
| `theme_config` | array | Theme configuration from theme.json |
| `theme_base_url` | string | Theme base URL (e.g. `/themes/default`) |
| `theme_css_files` | array | List of CSS files |
| `theme_js_files` | array | List of JS files |
| `theme_favicon` | string | Favicon URL |
| `plugin_css_urls` | array | CSS URLs from plugins (loaded after theme CSS) |
### Theme colors
| Variable | Type | Description |
|----------|------|-------------|
| `header_color` | string | Header background color |
| `header_font_color` | string | Header text color |
| `header_height` | string | Header height in px |
| `navigation_color` | string | Navigation background color |
| `navigation_font_color` | string | Navigation text color |
| `nav_height` | string | Navigation height in px |
| `sidebar_background` | string | Sidebar background color |
| `sidebar_border` | string | Sidebar border color |
### File info
| Variable | Type | Description |
|----------|------|-------------|
| `created` | string | File creation date |
| `modified` | string | File modification date |
| `show_created` | bool | Whether to show creation date |
| `file_info_block` | bool | Whether to show file info block |
### Translations
| Variable | Type | Description |
|----------|------|-------------|
| `t_home` | string | "Home" translation |
| `t_search` | string | "Search" translation |
| `t_search_placeholder` | string | Search placeholder translation |
| `t_search_button` | string | Search button translation |
| `t_welcome` | string | "Welcome" translation |
| `t_created` | string | "Created" translation |
| `t_modified` | string | "Modified" translation |
| `t_author` | string | "Author" translation |
| `t_manual` | string | "Manual" translation |
| `t_guide` | string | "Guide" translation |
| `t_no_content` | string | "No content" translation |
| `t_no_results` | string | "No results" translation |
| `t_results_found` | string | "Results found" translation |
| `t_powered_by` | string | "Powered by" translation |
| `t_page_not_found` | string | "Page not found" translation |
| `t_page_not_found_text` | string | "Page not found" text translation |
| `t_mappen` | string | "Folders" translation |
| `t_paginas` | string | "Pages" translation |
## Layouts and blocks
A layout template extends `base.twig` and fills the `content` block:
```twig
{% extends 'base.twig' %}
{% block content %}
<div class="container">
{{ content|raw }}
</div>
{% endblock %}
```
## Conditional sidebar
```twig
{% if sidebar_content %}
<div class="row">
<aside class="col-md-4">
{{ sidebar_content|raw }}
</aside>
<main class="col-md-8">
{{ content|raw }}
</main>
</div>
{% else %}
<div class="container">
{{ content|raw }}
</div>
{% endif %}
```
## Language switcher
```twig
{% for lang in available_langs %}
<a href="{{ lang.url }}" class="{{ lang.is_current ? 'active' : '' }}">
{{ lang.name }}
</a>
{% endfor %}
```
## Plugin CSS loading
Plugin CSS is automatically loaded after theme CSS, so themes can override plugin styling:
```twig
{% for cssUrl in plugin_css_urls|default([]) %}
<link href="{{ cssUrl }}" rel="stylesheet">
{% endfor %}
```