Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ab18c7b46 | ||
|
|
92d782e6c5 |
+29
-16
@@ -22,6 +22,7 @@ $layoutSidebarColor = $layoutThemeConfig['header_color'] ?? '#0a369d';
|
|||||||
.admin-sidebar .nav-link:hover { color: #fff; background-color: rgba(255,255,255,0.1); }
|
.admin-sidebar .nav-link:hover { color: #fff; background-color: rgba(255,255,255,0.1); }
|
||||||
.admin-sidebar .nav-link.active { color: #fff; background-color: rgba(255,255,255,0.2); border-left: 3px solid #fff; }
|
.admin-sidebar .nav-link.active { color: #fff; background-color: rgba(255,255,255,0.2); border-left: 3px solid #fff; }
|
||||||
.admin-sidebar .nav-link i { width: 24px; text-align: center; margin-right: 0.5rem; }
|
.admin-sidebar .nav-link i { width: 24px; text-align: center; margin-right: 0.5rem; }
|
||||||
|
.admin-sidebar .nav-section { color: rgba(255,255,255,0.4); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; padding: 1rem 1.25rem 0.3rem 1.25rem; }
|
||||||
.admin-main { margin-left: 240px; padding: 2rem; }
|
.admin-main { margin-left: 240px; padding: 2rem; }
|
||||||
.admin-brand { color: #fff; padding: 1.25rem; font-size: 1.1rem; border-bottom: 1px solid rgba(255,255,255,0.15); }
|
.admin-brand { color: #fff; padding: 1.25rem; font-size: 1.1rem; border-bottom: 1px solid rgba(255,255,255,0.15); }
|
||||||
.admin-brand i { margin-right: 0.5rem; }
|
.admin-brand i { margin-right: 0.5rem; }
|
||||||
@@ -45,37 +46,50 @@ $layoutSidebarColor = $layoutThemeConfig['header_color'] ?? '#0a369d';
|
|||||||
<i class="bi bi-gear-fill"></i> CodePress Admin
|
<i class="bi bi-gear-fill"></i> CodePress Admin
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav flex-column mt-2">
|
<ul class="nav flex-column mt-2">
|
||||||
|
<li class="nav-section">Algemeen</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'dashboard' || ($route ?? '') === '' ? 'active' : '' ?>" href="/admin/dashboard">
|
<a class="nav-link <?= ($route ?? '') === 'dashboard' || ($route ?? '') === '' ? 'active' : '' ?>" href="/admin/dashboard">
|
||||||
<i class="bi bi-speedometer2"></i> Dashboard
|
<i class="bi bi-speedometer2"></i> Dashboard
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-section">Content</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'content' || str_starts_with($route ?? '', 'content') ? 'active' : '' ?>" href="/admin/content">
|
<a class="nav-link <?= ($route ?? '') === 'content' || str_starts_with($route ?? '', 'content') ? 'active' : '' ?>" href="/admin/content">
|
||||||
<i class="bi bi-file-earmark-text"></i> Content
|
<i class="bi bi-file-earmark-text"></i> Content
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-section">Instellingen</li>
|
||||||
<a class="nav-link <?= ($route ?? '') === 'theme' ? 'active' : '' ?>" href="/admin/theme">
|
|
||||||
<i class="bi bi-palette"></i> Thema
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'config' ? 'active' : '' ?>" href="/admin/config">
|
<a class="nav-link <?= ($route ?? '') === 'config' ? 'active' : '' ?>" href="/admin/config">
|
||||||
<i class="bi bi-sliders"></i> Configuratie
|
<i class="bi bi-sliders"></i> Configuratie
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($route ?? '') === 'theme' ? 'active' : '' ?>" href="/admin/theme">
|
||||||
|
<i class="bi bi-palette"></i> Thema
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'security' ? 'active' : '' ?>" href="/admin/security">
|
<a class="nav-link <?= ($route ?? '') === 'security' ? 'active' : '' ?>" href="/admin/security">
|
||||||
<i class="bi bi-shield-check"></i> Beveiliging
|
<i class="bi bi-shield-check"></i> Beveiliging
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-section">Gegevens</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'statistics' ? 'active' : '' ?>" href="/admin/statistics">
|
<a class="nav-link <?= ($route ?? '') === 'statistics' ? 'active' : '' ?>" href="/admin/statistics">
|
||||||
<i class="bi bi-bar-chart"></i> Statistieken
|
<i class="bi bi-bar-chart"></i> Statistieken
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($route ?? '') === 'logs' ? 'active' : '' ?>" href="/admin/logs">
|
||||||
|
<i class="bi bi-journal-text"></i> Logs
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-section">Systeem</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'plugins' ? 'active' : '' ?>" href="/admin/plugins">
|
<a class="nav-link <?= ($route ?? '') === 'plugins' ? 'active' : '' ?>" href="/admin/plugins">
|
||||||
<i class="bi bi-plug"></i> Plugins
|
<i class="bi bi-plug"></i> Plugins
|
||||||
@@ -86,22 +100,21 @@ $layoutSidebarColor = $layoutThemeConfig['header_color'] ?? '#0a369d';
|
|||||||
<i class="bi bi-people"></i> Gebruikers
|
<i class="bi bi-people"></i> Gebruikers
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link <?= ($route ?? '') === 'guide' ? 'active' : '' ?>" href="/admin/guide">
|
|
||||||
<i class="bi bi-book"></i> Handleiding
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link <?= ($route ?? '') === 'logs' ? 'active' : '' ?>" href="/admin/logs">
|
|
||||||
<i class="bi bi-journal-text"></i> Logs
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= ($route ?? '') === 'update' ? 'active' : '' ?>" href="/admin/update">
|
<a class="nav-link <?= ($route ?? '') === 'update' ? 'active' : '' ?>" href="/admin/update">
|
||||||
<i class="bi bi-cloud-arrow-down"></i> Update
|
<i class="bi bi-cloud-arrow-down"></i> Update
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item mt-3">
|
|
||||||
|
<li class="nav-section">Help</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($route ?? '') === 'guide' ? 'active' : '' ?>" href="/admin/guide">
|
||||||
|
<i class="bi bi-book"></i> Handleiding
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-section mt-3">Links</li>
|
||||||
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/" target="_blank">
|
<a class="nav-link" href="/" target="_blank">
|
||||||
<i class="bi bi-box-arrow-up-right"></i> Website bekijken
|
<i class="bi bi-box-arrow-up-right"></i> Website bekijken
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -127,6 +127,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card shadow-sm mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
<i class="bi bi-eye-slash"></i> IP Uitsluitingen
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="excluded_ips" class="form-label fw-bold">IP-adressen uitsluiten van statistieken en beveiliging</label>
|
||||||
|
<textarea class="form-control font-monospace" id="excluded_ips" name="excluded_ips" rows="4" placeholder="Één IP per regel (bijv. 127.0.0.1)"><?= htmlspecialchars(implode("\n", $configData['analytics']['excluded_ips'] ?? [])) ?></textarea>
|
||||||
|
<div class="form-text">Verzoeken van deze IP-adressen worden niet opgenomen in de statistieken en overgeslagen bij beveiligingscontroles (bot-detectie, rate limiting).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary btn-lg">
|
<button type="submit" class="btn btn-primary btn-lg">
|
||||||
<i class="bi bi-check-lg"></i> Configuratie opslaan
|
<i class="bi bi-check-lg"></i> Configuratie opslaan
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
+3
-1
@@ -53,7 +53,8 @@ if (!file_exists($configJsonPath)) {
|
|||||||
'geoip_mmdb_path' => '',
|
'geoip_mmdb_path' => '',
|
||||||
'geoip_api_url' => '',
|
'geoip_api_url' => '',
|
||||||
'geoip_api_key' => '',
|
'geoip_api_key' => '',
|
||||||
'retention_days' => 400
|
'retention_days' => 400,
|
||||||
|
'excluded_ips' => []
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
@file_put_contents($configJsonPath, json_encode($defaultConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
@file_put_contents($configJsonPath, json_encode($defaultConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
@@ -87,6 +88,7 @@ if (file_exists($configJsonPath)) {
|
|||||||
'geoip_api_url' => '',
|
'geoip_api_url' => '',
|
||||||
'geoip_api_key' => '',
|
'geoip_api_key' => '',
|
||||||
'retention_days' => 400,
|
'retention_days' => 400,
|
||||||
|
'excluded_ips' => [],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
foreach ($sectionDefaults as $section => $defaults) {
|
foreach ($sectionDefaults as $section => $defaults) {
|
||||||
|
|||||||
+382
-428
@@ -3,22 +3,36 @@
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [Features](#features)
|
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Project Structure](#project-structure)
|
- [Project Structure](#project-structure)
|
||||||
- [Configuration](#configuration)
|
|
||||||
|
- [Content](#content)
|
||||||
- [Content Structure](#content-structure)
|
- [Content Structure](#content-structure)
|
||||||
- [Admin Console](#admin-console)
|
- [Content API (for PHP content files)](#content-api-for-php-content-files)
|
||||||
- [Logging & Request Log](#logging--request-log)
|
|
||||||
|
- [Settings](#settings)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Themes](#themes)
|
||||||
|
- [Security](#security)
|
||||||
|
|
||||||
|
- [Data](#data)
|
||||||
|
- [Statistics & Analytics](#statistics--analytics)
|
||||||
|
- [Logging](#logging)
|
||||||
|
|
||||||
|
- [System](#system)
|
||||||
|
- [Plugin System](#plugin-system)
|
||||||
|
- [User Management](#user-management)
|
||||||
|
- [Update](#update)
|
||||||
|
|
||||||
|
- [Guide (in Admin)](#guide-in-admin)
|
||||||
|
|
||||||
|
- [Other](#other)
|
||||||
- [Templates](#templates)
|
- [Templates](#templates)
|
||||||
- [URL Structure](#url-structure)
|
- [URL Structure](#url-structure)
|
||||||
- [SEO Optimization](#seo-optimization)
|
- [SEO Optimization](#seo-optimization)
|
||||||
- [Plugin System](#plugin-system)
|
|
||||||
- [Content API](#content-api-for-php-content-files)
|
|
||||||
- [Analytics & Tracking](#analytics--tracking)
|
|
||||||
- [Frequently Asked Questions](#frequently-asked-questions)
|
- [Frequently Asked Questions](#frequently-asked-questions)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Security](#security)
|
|
||||||
- [Version](#version)
|
- [Version](#version)
|
||||||
- [Support](#support)
|
- [Support](#support)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
@@ -27,72 +41,6 @@
|
|||||||
|
|
||||||
CodePress CMS is a lightweight, file-based content management system built with PHP (>=8.0). Works without a database.
|
CodePress CMS is a lightweight, file-based content management system built with PHP (>=8.0). Works without a database.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Navigation
|
|
||||||
- Tab-style navigation with Bootstrap 5 styling
|
|
||||||
- Dropdown menus for folders and sub-folders
|
|
||||||
- Home button with icon
|
|
||||||
- Automatic menu generation based on directory structure
|
|
||||||
- Responsive design
|
|
||||||
- Breadcrumb navigation with sidebar toggle
|
|
||||||
- Active state marking
|
|
||||||
- **Sidebar toggle** - Button placed left of HOME in the breadcrumb to open/close the sidebar. The icon changes between open and closed state. The choice is preserved during the session
|
|
||||||
|
|
||||||
### Content Types
|
|
||||||
- **Markdown (.md)** - CommonMark support via `league/commonmark`
|
|
||||||
- **PHP (.php)** - Dynamic content with **Content API** (`$api` variable)
|
|
||||||
- **HTML (.html)** - Static HTML pages
|
|
||||||
- **Directory listings** - Automatic directory overviews
|
|
||||||
- **Language-specific content** - `en.` and `nl.` prefixes
|
|
||||||
|
|
||||||
### Search Functionality
|
|
||||||
- Full-text search through all content
|
|
||||||
- Results with snippets and highlighting
|
|
||||||
- Direct navigation to found pages
|
|
||||||
- SEO-friendly search results
|
|
||||||
- Search URL: `?search=query`
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
- **Settings form** in admin panel via `/admin/config`
|
|
||||||
- Dynamic homepage setting via dropdown (auto, newest modified page, or manual selection)
|
|
||||||
- SEO settings (description, keywords)
|
|
||||||
- Author information with links
|
|
||||||
- Theme configuration via `themes/` directory
|
|
||||||
- Language settings
|
|
||||||
- Feature toggles (auto_link_pages, search_enabled, breadcrumbs_enabled)
|
|
||||||
|
|
||||||
### Layout & Design
|
|
||||||
- Flexbox layout for responsive structure
|
|
||||||
- Fixed header with logo and search
|
|
||||||
- Breadcrumb navigation
|
|
||||||
- Fixed footer with file info and links
|
|
||||||
- Bootstrap 5 styling
|
|
||||||
- Mustache templates (`cms/templates/`)
|
|
||||||
- Semantic HTML5 structure
|
|
||||||
- **Dynamic layouts** with YAML frontmatter
|
|
||||||
- **Sidebar support** with plugin integration and toggle function via breadcrumb
|
|
||||||
- **Theme support** via `themes/` directory with theme.json per theme
|
|
||||||
|
|
||||||
### Admin Console
|
|
||||||
- Built-in admin panel at `/admin`
|
|
||||||
- **Dashboard** with statistics (pages, directories, plugins) and quick actions
|
|
||||||
- **Content management** - Browse files, upload, create, edit, rename, move and delete
|
|
||||||
- **CodeMirror editor** with toolbar (bold, italic, heading, link, image, list, media insert)
|
|
||||||
- **Media browser modal** in the editor for selecting images/files
|
|
||||||
- **Directory management** - Create, rename and delete directories (empty only)
|
|
||||||
- **Configuration editor** - Settings form for site configuration (title, homepage, language, SEO, author, features)
|
|
||||||
- **Theme management** - Create, activate, edit (colors, background) and delete themes
|
|
||||||
- **Plugin management** - Overview, create, edit, configure, enable/disable and delete
|
|
||||||
- **Media management** - Upload and delete media files in `content/-assets/`
|
|
||||||
- **User management** - Add, remove users, change passwords
|
|
||||||
- **Guide** - Built-in documentation with API reference
|
|
||||||
- **Logging** - Activity log and request log with IP, page, domain; view and download via `/admin/logs`
|
|
||||||
- **Bot & AI blocking** - Automatic 403 for known bots and AI crawlers
|
|
||||||
- Session-based authentication with bcrypt hashing
|
|
||||||
- CSRF protection, brute-force lockout (5 attempts, 15 min)
|
|
||||||
- Default login: `admin` / `admin` (change immediately after installation)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Upload files to web server
|
1. Upload files to web server
|
||||||
@@ -111,7 +59,12 @@ codepress/
|
|||||||
│ │ ├── class/
|
│ │ ├── class/
|
||||||
│ │ │ ├── CodePressCMS.php # Main CMS class (content, navigation, search)
|
│ │ │ ├── CodePressCMS.php # Main CMS class (content, navigation, search)
|
||||||
│ │ │ ├── Logger.php # Structured logging system
|
│ │ │ ├── Logger.php # Structured logging system
|
||||||
│ │ │ └── SimpleTemplate.php # Mustache-style template engine
|
│ │ │ ├── SimpleTemplate.php # Mustache-style template engine
|
||||||
|
│ │ │ ├── Analytics.php # Visitor statistics
|
||||||
|
│ │ │ ├── BotGuard.php # Bot/AI/scraper detection
|
||||||
|
│ │ │ ├── GeoIP.php # Country lookup by IP
|
||||||
|
│ │ │ ├── Cache.php # File-based caching
|
||||||
|
│ │ │ └── RateLimiter.php # Per-IP rate limiting
|
||||||
│ │ ├── plugin/
|
│ │ ├── plugin/
|
||||||
│ │ │ ├── PluginManager.php # Plugin loader and manager
|
│ │ │ ├── PluginManager.php # Plugin loader and manager
|
||||||
│ │ │ └── CMSAPI.php # API for plugin developers
|
│ │ │ └── CMSAPI.php # API for plugin developers
|
||||||
@@ -120,7 +73,6 @@ codepress/
|
|||||||
│ ├── lang/ # Language files
|
│ ├── lang/ # Language files
|
||||||
│ │ ├── nl.php # Dutch translations
|
│ │ ├── nl.php # Dutch translations
|
||||||
│ │ └── en.php # English translations
|
│ │ └── en.php # English translations
|
||||||
│ ├── logs/ # CMS logs
|
|
||||||
│ ├── templates/ # Mustache templates
|
│ ├── templates/ # Mustache templates
|
||||||
│ │ ├── layout.mustache # Main layout (CSS, structure)
|
│ │ ├── layout.mustache # Main layout (CSS, structure)
|
||||||
│ │ ├── assets/ # Header, navigation, footer partials
|
│ │ ├── assets/ # Header, navigation, footer partials
|
||||||
@@ -144,14 +96,18 @@ codepress/
|
|||||||
│ │ ├── content-new.php # Create new content
|
│ │ ├── content-new.php # Create new content
|
||||||
│ │ ├── content-dir-form.php # Create/edit directory
|
│ │ ├── content-dir-form.php # Create/edit directory
|
||||||
│ │ ├── content-move-form.php # Move content
|
│ │ ├── content-move-form.php # Move content
|
||||||
│ │ ├── config.php # Configuration editor (JSON)
|
│ │ ├── config.php # Configuration editor
|
||||||
|
│ │ ├── security.php # Security settings
|
||||||
|
│ │ ├── statistics.php # Statistics dashboard
|
||||||
│ │ ├── plugins.php # Plugin overview
|
│ │ ├── plugins.php # Plugin overview
|
||||||
│ │ ├── plugins-edit.php # Plugin PHP source code editor
|
│ │ ├── plugins-edit.php # Plugin PHP source code editor
|
||||||
│ │ ├── plugins-new.php # Create new plugin
|
│ │ ├── plugins-new.php # Create new plugin
|
||||||
│ │ ├── plugin-config.php # Plugin configuration editor
|
│ │ ├── plugin-config.php # Plugin configuration editor
|
||||||
│ │ ├── theme.php # Theme management (create, activate, edit)
|
│ │ ├── theme.php # Theme management
|
||||||
│ │ ├── media.php # Media management (upload, delete)
|
│ │ ├── users.php # User management
|
||||||
│ │ └── users.php # User management
|
│ │ ├── logs.php # Log viewer
|
||||||
|
│ │ ├── update.php # System update
|
||||||
|
│ │ └── guide.php # Guide
|
||||||
│ └── storage/logs/ # Admin logs
|
│ └── storage/logs/ # Admin logs
|
||||||
├── cli/ # CLI scripts & tests
|
├── cli/ # CLI scripts & tests
|
||||||
├── content/ # Content files
|
├── content/ # Content files
|
||||||
@@ -160,8 +116,7 @@ codepress/
|
|||||||
│ └── ... # Other content
|
│ └── ... # Other content
|
||||||
├── plugins/ # CMS plugins
|
├── plugins/ # CMS plugins
|
||||||
│ ├── HTMLBlock/ # Custom HTML blocks in sidebar
|
│ ├── HTMLBlock/ # Custom HTML blocks in sidebar
|
||||||
│ ├── MQTTTracker/ # Real-time analytics and tracking
|
│ └── MQTTTracker/ # Real-time analytics and tracking
|
||||||
│ └── test/ # Test plugin
|
|
||||||
├── public/ # Web root
|
├── public/ # Web root
|
||||||
│ ├── index.php # Website entry point (media serving + CMS)
|
│ ├── index.php # Website entry point (media serving + CMS)
|
||||||
│ ├── admin.php # Admin entry point + routing
|
│ ├── admin.php # Admin entry point + routing
|
||||||
@@ -174,28 +129,173 @@ codepress/
|
|||||||
├── themes/ # Theme definitions
|
├── themes/ # Theme definitions
|
||||||
│ ├── default/ # Default theme
|
│ ├── default/ # Default theme
|
||||||
│ │ └── theme.json # Colors, heights, background
|
│ │ └── theme.json # Colors, heights, background
|
||||||
│ └── test/ # Test theme
|
│ └── ... # Other themes
|
||||||
│ └── theme.json
|
|
||||||
├── config.json # Site configuration
|
├── config.json # Site configuration
|
||||||
├── version.php # Version information (1.6.0)
|
├── version.php # Version information
|
||||||
└── vendor/ # Composer dependencies
|
└── vendor/ # Composer dependencies
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
---
|
||||||
|
|
||||||
### Basic Configuration
|
## Content
|
||||||
|
|
||||||
The site configuration is managed via the **admin panel** at `/admin/config`. Here you'll find a settings form with the following sections:
|
### Content Structure
|
||||||
|
|
||||||
|
#### File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
content/
|
||||||
|
├── folder1/
|
||||||
|
│ ├── subfolder1/
|
||||||
|
│ │ ├── nl.page1.md
|
||||||
|
│ │ └── en.page1.md
|
||||||
|
│ └── page3.html
|
||||||
|
├── folder2/
|
||||||
|
│ └── page4.md
|
||||||
|
├── index.md
|
||||||
|
└── -assets/
|
||||||
|
├── image.jpg
|
||||||
|
└── document.pdf
|
||||||
|
```
|
||||||
|
|
||||||
|
#### File Naming
|
||||||
|
- Use lowercase filenames
|
||||||
|
- No spaces - use `-` or `_`
|
||||||
|
- Logical extensions - `.md`, `.php`, `.html`
|
||||||
|
- Unique names - no duplicates
|
||||||
|
- Language prefixes - `nl.file.md` and `en.file.md`
|
||||||
|
|
||||||
|
#### Media Files
|
||||||
|
|
||||||
|
Media files (images, PDFs, video, audio) can be placed in any `content/` subdirectory and are served via:
|
||||||
|
|
||||||
|
- **`/-media/path/file.jpg`** - Media from any content subdirectory
|
||||||
|
- **`/-assets/file.jpg`** - Backward compatibility (old URLs)
|
||||||
|
- Uploads via the admin panel go to `content/-assets/`
|
||||||
|
|
||||||
|
### Content API (for PHP content files)
|
||||||
|
|
||||||
|
PHP content files (`.php` in the `content/` directory) have access to an `$api` variable with the following methods:
|
||||||
|
|
||||||
|
#### Getting pages
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get all pages with titles
|
||||||
|
$pages = $api->getAllPages();
|
||||||
|
// Result: ['index' => 'Home', 'about' => 'About Us', ...]
|
||||||
|
|
||||||
|
// Get a specific page's content
|
||||||
|
$page = $api->getPage('about');
|
||||||
|
// $page['title'], $page['content'], $page['path'], $page['layout'], $page['metadata']
|
||||||
|
|
||||||
|
// Check if a page exists
|
||||||
|
if ($api->pageExists('contact')) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Navigation
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get menu structure
|
||||||
|
$menu = $api->getMenu();
|
||||||
|
// Nested array with 'title', 'path', 'url', 'children'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get config value (dot notation)
|
||||||
|
$title = $api->getConfig('site_title');
|
||||||
|
$lang = $api->getConfig('language.default');
|
||||||
|
$seoDesc = $api->getConfig('seo.description', 'Default description');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Current page
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Current page title
|
||||||
|
$pageTitle = $api->getCurrentPageTitle();
|
||||||
|
|
||||||
|
// Current page path
|
||||||
|
$pagePath = $api->getCurrentPagePath();
|
||||||
|
|
||||||
|
// Check if this is the homepage
|
||||||
|
if ($api->isHomepage()) {
|
||||||
|
echo 'Welcome!';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### URLs and language
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Build URL for a page
|
||||||
|
$url = $api->buildUrl('about', 'en');
|
||||||
|
|
||||||
|
// Current language
|
||||||
|
$lang = $api->getCurrentLanguage();
|
||||||
|
|
||||||
|
// Available languages
|
||||||
|
$languages = $api->getAvailableLanguages();
|
||||||
|
|
||||||
|
// Site title
|
||||||
|
$title = $api->getSiteTitle();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Translations and search
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get translation
|
||||||
|
$label = $api->t('home');
|
||||||
|
|
||||||
|
// Search results (if searching)
|
||||||
|
if ($api->isSearching()) {
|
||||||
|
$results = $api->getSearchResults();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example PHP content file
|
||||||
|
|
||||||
|
```php
|
||||||
|
---
|
||||||
|
title: Page Overview
|
||||||
|
layout: content
|
||||||
|
---
|
||||||
|
<h1>All Pages</h1>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($api->getAllPages() as $path => $title): ?>
|
||||||
|
<li><a href="<?= $api->buildUrl($path) ?>"><?= htmlspecialchars($title) ?></a></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The site configuration is managed via the **admin panel** at `/admin/config`. The form includes:
|
||||||
|
|
||||||
- **General settings** - Site title and homepage (dropdown with available pages)
|
- **General settings** - Site title and homepage (dropdown with available pages)
|
||||||
- **Language** - Default language and available languages
|
- **Language** - Default language and available languages
|
||||||
- **SEO** - Meta description and keywords
|
- **SEO** - Meta description and keywords
|
||||||
- **Author** - Name and website
|
- **Author** - Name and website
|
||||||
- **Features** - Auto-link pages, search, breadcrumbs, show version
|
- **Features** - Auto-link pages, search, breadcrumbs, show version
|
||||||
|
- **IP Exclusions** - Exclude IP addresses from statistics and security checks
|
||||||
|
|
||||||
The configuration is stored in `config.json`. You can also edit this file manually for advanced options like `active_theme`, `content_dir` and `templates_dir`.
|
The configuration is stored in `config.json`. You can also edit this file manually for advanced options.
|
||||||
|
|
||||||
### Example `config.json`
|
#### IP Exclusions
|
||||||
|
|
||||||
|
Under **Configuration** in the admin panel, the "IP Exclusions" field lets you specify IP addresses that will be:
|
||||||
|
|
||||||
|
- Excluded from visitor statistics
|
||||||
|
- Skipped during all security checks (bot detection, rate limiting, IP blocklist)
|
||||||
|
|
||||||
|
This is useful for your own IP address or internal monitoring tools.
|
||||||
|
|
||||||
|
#### Example `config.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -220,11 +320,25 @@ The configuration is stored in `config.json`. You can also edit this file manual
|
|||||||
"auto_link_pages": true,
|
"auto_link_pages": true,
|
||||||
"search_enabled": true,
|
"search_enabled": true,
|
||||||
"breadcrumbs_enabled": true
|
"breadcrumbs_enabled": true
|
||||||
|
},
|
||||||
|
"analytics": {
|
||||||
|
"enabled": true,
|
||||||
|
"excluded_ips": ["127.0.0.1", "::1"]
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"block_ai_bots": true,
|
||||||
|
"block_scrapers": true,
|
||||||
|
"block_empty_user_agent": true,
|
||||||
|
"rate_limit_enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Theme Configuration (`themes/<name>/theme.json`)
|
### Themes
|
||||||
|
|
||||||
|
Themes are managed via the admin panel at `/admin/theme`. You can create, activate, adjust colors, upload background images, and delete themes.
|
||||||
|
|
||||||
|
#### Theme Configuration (`themes/<name>/theme.json`)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -242,232 +356,82 @@ The configuration is stored in `config.json`. You can also edit this file manual
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Themes can be managed via the admin panel: create, activate, adjust colors, upload background image and delete.
|
#### How to create a new theme
|
||||||
|
|
||||||
## Content Structure
|
1. Go to `/admin/theme`
|
||||||
|
2. Enter a name and click "Create"
|
||||||
|
3. Adjust colors, heights and background
|
||||||
|
4. Activate the theme
|
||||||
|
|
||||||
### File Structure
|
### Security
|
||||||
|
|
||||||
```
|
Security settings are managed via `/admin/security`. Includes:
|
||||||
content/
|
|
||||||
├── folder1/
|
|
||||||
│ ├── subfolder1/
|
|
||||||
│ │ ├── nl.page1.md
|
|
||||||
│ │ └── en.page1.md
|
|
||||||
│ └── page3.html
|
|
||||||
├── folder2/
|
|
||||||
│ └── page4.md
|
|
||||||
├── index.md
|
|
||||||
└── -assets/
|
|
||||||
├── image.jpg
|
|
||||||
└── document.pdf
|
|
||||||
```
|
|
||||||
|
|
||||||
### File Naming
|
#### Bot, AI & Scraper Blocking
|
||||||
- Use lowercase filenames
|
|
||||||
- No spaces - use `-` or `_`
|
|
||||||
- Logical extensions - `.md`, `.php`, `.html`
|
|
||||||
- Unique names - no duplicates
|
|
||||||
- Language prefixes - `nl.file.md` and `en.file.md`
|
|
||||||
|
|
||||||
### Media Files
|
|
||||||
|
|
||||||
Media files (images, PDFs, video, audio) can be placed in any `content/` subdirectory and are served via:
|
|
||||||
|
|
||||||
- **`/-media/path/file.jpg`** - Media from any content subdirectory
|
|
||||||
- **`/-assets/file.jpg`** - Backward compatibility (old URLs)
|
|
||||||
- Uploads via the admin panel go to `content/-assets/`
|
|
||||||
|
|
||||||
## Admin Console
|
|
||||||
|
|
||||||
### Access
|
|
||||||
|
|
||||||
- **URL**: `/admin`
|
|
||||||
- **Default login**: `admin` / `admin`
|
|
||||||
|
|
||||||
### Routes
|
|
||||||
|
|
||||||
| Route | Description |
|
|
||||||
|---|---|
|
|
||||||
| `login` | Login page |
|
|
||||||
| `logout` | Logout |
|
|
||||||
| `dashboard` | Dashboard with statistics |
|
|
||||||
| `content` | Content overview (browse, upload) |
|
|
||||||
| `content-edit` | Edit content (CodeMirror editor) |
|
|
||||||
| `content-new` | Create new content |
|
|
||||||
| `content-delete` | Delete content |
|
|
||||||
| `content-move` | Move content to another directory |
|
|
||||||
| `content-dir-form` | Create or edit directory |
|
|
||||||
| `content-dir-rename` | Rename directory |
|
|
||||||
| `content-dir-create` | Create directory (POST) |
|
|
||||||
| `content-dir-delete` | Delete directory (empty only) |
|
|
||||||
| `config` | Configuration form (title, homepage, language, SEO, author, features) |
|
|
||||||
| `security` | Security settings (bot/AI blocking, rate limiting, IP block/allowlist) |
|
|
||||||
| `statistics` | Visitor statistics with world map, countries, pages and GeoIP settings |
|
|
||||||
| `update` | One-click system update via Git pull |
|
|
||||||
| `theme` | Theme management |
|
|
||||||
| `plugins` | Plugin overview |
|
|
||||||
| `plugins-new` | Create new plugin |
|
|
||||||
| `plugins-edit` | Edit plugin PHP source code |
|
|
||||||
| `plugins-config` | Edit plugin configuration |
|
|
||||||
| `plugins-toggle` | Enable/disable plugin |
|
|
||||||
| `plugins-delete` | Delete plugin |
|
|
||||||
| `media` | Media management (upload, delete) |
|
|
||||||
| `media-list` | JSON list of all media (for editor modal) |
|
|
||||||
| `users` | User management |
|
|
||||||
| `guide` | Guide / documentation |
|
|
||||||
| `logs` | Log viewer (activity + requests) |
|
|
||||||
|
|
||||||
### Editor Features
|
|
||||||
|
|
||||||
The content editor (`content-edit`, `content-new`) includes:
|
|
||||||
|
|
||||||
- **CodeMirror** syntax highlighting for Markdown/PHP/HTML
|
|
||||||
- **Toolbar** with buttons for: bold, italic, heading, link, image, list, media insert
|
|
||||||
- **Media browser modal** - Open via the "Media" button to select images/files
|
|
||||||
- **Inline filename rename** - Inline filename input with extension badge
|
|
||||||
- **Layout selector** - Choose from available layouts
|
|
||||||
- **Plugin per-page visibility** - Select which plugins to show on this page
|
|
||||||
- **Upload and "Create" buttons** are disabled until input is provided
|
|
||||||
- **"Cancel" button** (instead of "Back") for unsaved changes
|
|
||||||
|
|
||||||
### Logging & Request Log
|
|
||||||
|
|
||||||
The admin console maintains two logs:
|
|
||||||
|
|
||||||
- **Activity log** (`admin/storage/logs/admin.log`) — admin actions like creating, editing, deleting pages, enabling/disabling plugins, changing configuration.
|
|
||||||
- **Request log** (`admin/storage/logs/requests.log`) — every page view on the website, including IP, page, domain, language, user agent, and referrer.
|
|
||||||
|
|
||||||
Both logs can be viewed and downloaded via **`/admin/logs`** or the sidebar "Logs" link. The dashboard shows the last 20 entries of each log. Click "View all →" for the full list, where you can also download or clear.
|
|
||||||
|
|
||||||
### Bot & AI Blocking
|
|
||||||
|
|
||||||
Incoming requests are checked against known bot and AI crawler patterns via the User-Agent header. Detected bots receive a **403 Forbidden** response.
|
Incoming requests are checked against known bot and AI crawler patterns via the User-Agent header. Detected bots receive a **403 Forbidden** response.
|
||||||
|
|
||||||
Blocked categories:
|
|
||||||
|
|
||||||
| Category | Examples |
|
| Category | Examples |
|
||||||
|---|---|
|
|---|---|
|
||||||
| AI Crawlers | GPTBot, ChatGPT-User, Claude-Web, ClaudeBot, Google-Extended, CCBot, PerplexityBot |
|
| AI Crawlers | GPTBot, ChatGPT-User, Claude-Web, ClaudeBot, Google-Extended, CCBot, PerplexityBot |
|
||||||
| Search Engines | Googlebot, Bingbot, BingPreview, DuckDuckBot, YandexBot, Baiduspider |
|
| Search Engines | Googlebot, Bingbot, BingPreview, DuckDuckBot, YandexBot, Baiduspider |
|
||||||
| Scrapers | HTTrack, Scrapy, PhantomJS |
|
| Scrapers | HTTrack, Scrapy, PhantomJS |
|
||||||
|
|
||||||
## Templates
|
#### Rate Limiting
|
||||||
|
|
||||||
### Template Variables
|
Prevents IPs from overloading the site. Returns HTTP 429 on exceedance.
|
||||||
|
|
||||||
#### Site Info
|
#### IP Lists
|
||||||
- `site_title` - Website title
|
|
||||||
- `author_name` - Author name
|
|
||||||
- `author_website` - Author website
|
|
||||||
- `author_git` - Git repository link
|
|
||||||
|
|
||||||
#### Page Info
|
- **IP Whitelist** - IPs on the whitelist are never blocked
|
||||||
- `page_title` - Page title
|
- **IP Blocklist** - IPs on the blocklist always receive a 403 Forbidden
|
||||||
- `content` - Content (HTML)
|
|
||||||
- `file_info` - File information
|
|
||||||
- `is_homepage` - Boolean: is this the homepage?
|
|
||||||
|
|
||||||
#### Navigation
|
#### Dynamic robots.txt
|
||||||
- `menu` - Navigation menu
|
|
||||||
- `breadcrumb` - Breadcrumb navigation
|
|
||||||
- `homepage` - Homepage link
|
|
||||||
|
|
||||||
#### Theme (from theme.json)
|
The system automatically generates a `robots.txt` based on your security settings, available at `/robots.txt`.
|
||||||
- `header_color` - Header background color
|
|
||||||
- `header_font_color` - Header text color
|
|
||||||
- `header_height` - Header height in pixels
|
|
||||||
- `navigation_color` - Navigation background color
|
|
||||||
- `navigation_font_color` - Navigation text color
|
|
||||||
- `nav_height` - Navigation height in pixels
|
|
||||||
- `sidebar_background` - Sidebar background color
|
|
||||||
- `sidebar_border` - Sidebar border color
|
|
||||||
- `background_image_css` - CSS for background image
|
|
||||||
- `background_image_opacity` - Overlay opacity
|
|
||||||
|
|
||||||
#### Language
|
|
||||||
- `current_lang` - Current language (en/nl)
|
|
||||||
- `current_lang_upper` - Current language (EN/NL)
|
|
||||||
- `t_*` - Translated strings
|
|
||||||
|
|
||||||
### Layout Options
|
|
||||||
|
|
||||||
Use YAML frontmatter to select layout:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
---
|
||||||
title: My Page
|
|
||||||
layout: sidebar-content
|
## Data
|
||||||
plugins: HTMLBlock
|
|
||||||
|
### Statistics & Analytics
|
||||||
|
|
||||||
|
The statistics dashboard is available at `/admin/statistics` and provides:
|
||||||
|
|
||||||
|
- **KPI cards** - Page views, unique visitors, human/bot ratio, blocked requests
|
||||||
|
- **World map** - Visual representation of visitors per country with color intensity
|
||||||
|
- **Countries list** - Top 25 countries with percentage
|
||||||
|
- **Most viewed pages** - Top 25 pages
|
||||||
|
- **Daily chart** - Bar chart of visitors per day
|
||||||
|
- **Referring sites** - Top 15 referrers
|
||||||
|
|
||||||
|
#### Periods and export
|
||||||
|
|
||||||
|
Filter by 7, 30, 90 days or all time. Export data as CSV or JSON.
|
||||||
|
|
||||||
|
#### GeoIP
|
||||||
|
|
||||||
|
Country detection via three sources:
|
||||||
|
- **Local (DB-IP Lite)** - Offline, privacy-friendly, auto-updated
|
||||||
|
- **MaxMind database (.mmdb)** - Custom MMDB file
|
||||||
|
- **External API** - Custom API URL and key
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
The admin console maintains two logs, viewable at `/admin/logs`:
|
||||||
|
|
||||||
|
- **Activity log** (`admin/storage/logs/admin.log`) — admin actions like creating, editing, deleting pages, enabling/disabling plugins, changing configuration.
|
||||||
|
- **Request log** (`admin/storage/logs/requests.log`) — every page view on the website, including IP, page, domain, language, user agent, and referrer.
|
||||||
|
|
||||||
|
The dashboard shows the last 20 entries of each log. Click "View all →" for the full list, where you can also download or clear.
|
||||||
|
|
||||||
---
|
---
|
||||||
```
|
|
||||||
|
|
||||||
### Available Layouts
|
## System
|
||||||
|
|
||||||
- `sidebar-content` - Sidebar left, content right (default)
|
### Plugin System
|
||||||
- `content` - Content only (full width)
|
|
||||||
- `sidebar` - Sidebar only
|
|
||||||
- `content-sidebar` - Content left, sidebar right
|
|
||||||
- `content-sidebar-reverse` - Content right, sidebar left
|
|
||||||
|
|
||||||
### Meta Data
|
#### Plugin Structure
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
title: Page Title
|
|
||||||
layout: content-sidebar
|
|
||||||
description: Page description
|
|
||||||
author: Author Name
|
|
||||||
date: 2025-11-26
|
|
||||||
plugins: HTMLBlock, MQTTTracker
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
## URL Structure
|
|
||||||
|
|
||||||
### Frontend Page URLs
|
|
||||||
- **Home**: `/` or `/en/`
|
|
||||||
- **Page**: `/en/folder/page`
|
|
||||||
- **Search**: `?search=query` (via search form)
|
|
||||||
- **Guide**: `/en/guide`
|
|
||||||
- **Language switch**: `/nl` or `/en`
|
|
||||||
|
|
||||||
### Media URLs
|
|
||||||
- **Media**: `/-media/path/to/file.jpg` (from any content subdirectory)
|
|
||||||
- **Assets**: `/-assets/file.jpg` (from content/-assets/, backward compatible)
|
|
||||||
|
|
||||||
### Admin URLs
|
|
||||||
- **Admin**: `/admin`
|
|
||||||
- **Dashboard**: `/admin/dashboard`
|
|
||||||
- **Edit content**: `/admin/content-edit?file=page.md`
|
|
||||||
- **New content**: `/admin/content-new?dir=folder`
|
|
||||||
- **Edit theme**: `/admin/theme?edit=themename`
|
|
||||||
|
|
||||||
## SEO Optimization
|
|
||||||
|
|
||||||
### Meta Tags
|
|
||||||
|
|
||||||
The CMS automatically adds meta tags:
|
|
||||||
```html
|
|
||||||
<meta name="generator" content="CodePress CMS">
|
|
||||||
<meta name="author" content="E. Noorlander">
|
|
||||||
<meta name="description" content="...">
|
|
||||||
<meta name="keywords" content="...">
|
|
||||||
```
|
|
||||||
|
|
||||||
### Security Headers
|
|
||||||
|
|
||||||
```http
|
|
||||||
X-Content-Type-Options: nosniff
|
|
||||||
X-Frame-Options: SAMEORIGIN
|
|
||||||
X-XSS-Protection: 1; mode=block
|
|
||||||
Referrer-Policy: strict-origin-when-cross-origin
|
|
||||||
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; ...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin System
|
|
||||||
|
|
||||||
### Plugin Structure
|
|
||||||
|
|
||||||
```
|
```
|
||||||
plugins/
|
plugins/
|
||||||
@@ -479,13 +443,9 @@ plugins/
|
|||||||
│ ├── MQTTTracker.php
|
│ ├── MQTTTracker.php
|
||||||
│ ├── config.json
|
│ ├── config.json
|
||||||
│ └── README.md
|
│ └── README.md
|
||||||
└── test/
|
|
||||||
├── test.php
|
|
||||||
├── config.json
|
|
||||||
└── README.md
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Plugin Development
|
#### Plugin Development
|
||||||
|
|
||||||
- **API access** via `CMSAPI` class - gives access to CMS configuration, templates, menu
|
- **API access** via `CMSAPI` class - gives access to CMS configuration, templates, menu
|
||||||
- **Sidebar content** with `getSidebarContent()` - returns HTML for sidebar
|
- **Sidebar content** with `getSidebarContent()` - returns HTML for sidebar
|
||||||
@@ -494,7 +454,7 @@ plugins/
|
|||||||
- **viewable** field in config.json determines if plugin is visible in sidebar
|
- **viewable** field in config.json determines if plugin is visible in sidebar
|
||||||
- **Per-page visibility** - via the editor plugin selector per page
|
- **Per-page visibility** - via the editor plugin selector per page
|
||||||
|
|
||||||
### Plugin Boilerplate
|
#### Plugin Boilerplate
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
@@ -533,196 +493,190 @@ class MyPlugin
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Available Plugins
|
#### Known Issue: MQTTTracker Credentials
|
||||||
|
|
||||||
- **HTMLBlock** - Custom HTML blocks in sidebar
|
|
||||||
- **MQTTTracker** - Real-time analytics and tracking via MQTT
|
|
||||||
- **test** - Example/test plugin
|
|
||||||
|
|
||||||
### Known Issue: MQTTTracker Credentials
|
|
||||||
|
|
||||||
The MQTTTracker plugin stores `broker_host`, `broker_port`, `client_id`, `username` and `password` in plain text in `plugins/MQTTTracker/config.json`. This is a known open security issue - in a production environment it is recommended to externalize these credentials to environment variables or a separate credential manager.
|
The MQTTTracker plugin stores `broker_host`, `broker_port`, `client_id`, `username` and `password` in plain text in `plugins/MQTTTracker/config.json`. This is a known open security issue - in a production environment it is recommended to externalize these credentials to environment variables or a separate credential manager.
|
||||||
|
|
||||||
## Content API (for PHP content files)
|
### User Management
|
||||||
|
|
||||||
PHP content files (`.php` in the `content/` directory) have access to an `$api` variable with the following methods:
|
Users are managed via `/admin/users`. Features:
|
||||||
|
- Add user with username, password and role
|
||||||
|
- Delete user
|
||||||
|
- Change password for other users (admin)
|
||||||
|
- Change own password (requires current password)
|
||||||
|
|
||||||
### Getting pages
|
Passwords are stored as bcrypt hashes in `admin/config/admin.json`.
|
||||||
|
|
||||||
```php
|
### Update
|
||||||
// Get all pages with titles
|
|
||||||
$pages = $api->getAllPages();
|
|
||||||
// Result: ['index' => 'Home', 'about' => 'About Us', ...]
|
|
||||||
|
|
||||||
// Get a specific page's content
|
Via `/admin/update` the system can be updated in one click via Git pull. The page shows the current version and git branch, and executes `git pull origin <branch>` on confirmation.
|
||||||
$page = $api->getPage('about');
|
|
||||||
// $page['title'], $page['content'], $page['path'], $page['layout'], $page['metadata']
|
|
||||||
|
|
||||||
// Check if a page exists
|
|
||||||
if ($api->pageExists('contact')) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Navigation
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Get menu structure
|
|
||||||
$menu = $api->getMenu();
|
|
||||||
// Nested array with 'title', 'path', 'url', 'children'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Get config value (dot notation)
|
|
||||||
$title = $api->getConfig('site_title');
|
|
||||||
$lang = $api->getConfig('language.default');
|
|
||||||
$seoDesc = $api->getConfig('seo.description', 'Default description');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Current page
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Current page title
|
|
||||||
$pageTitle = $api->getCurrentPageTitle();
|
|
||||||
|
|
||||||
// Current page path
|
|
||||||
$pagePath = $api->getCurrentPagePath();
|
|
||||||
|
|
||||||
// Check if this is the homepage
|
|
||||||
if ($api->isHomepage()) {
|
|
||||||
echo 'Welcome!';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### URLs and language
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Build URL for a page
|
|
||||||
$url = $api->buildUrl('about', 'en');
|
|
||||||
|
|
||||||
// Current language
|
|
||||||
$lang = $api->getCurrentLanguage();
|
|
||||||
|
|
||||||
// Available languages
|
|
||||||
$languages = $api->getAvailableLanguages();
|
|
||||||
|
|
||||||
// Site title
|
|
||||||
$title = $api->getSiteTitle();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Translations and search
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Get translation
|
|
||||||
$label = $api->t('home');
|
|
||||||
|
|
||||||
// Search results (if searching)
|
|
||||||
if ($api->isSearching()) {
|
|
||||||
$results = $api->getSearchResults();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example PHP content file
|
|
||||||
|
|
||||||
```php
|
|
||||||
---
|
---
|
||||||
title: Page Overview
|
|
||||||
layout: content
|
## Guide (in Admin)
|
||||||
|
|
||||||
|
This guide is also built into the admin panel via `/admin/guide`, with support for Dutch and English.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
|
||||||
|
#### Template Variables
|
||||||
|
|
||||||
|
**Site Info** - `site_title`, `author_name`, `author_website`, `author_git`
|
||||||
|
|
||||||
|
**Page Info** - `page_title`, `content`, `file_info`, `is_homepage`
|
||||||
|
|
||||||
|
**Navigation** - `menu`, `breadcrumb`, `homepage`
|
||||||
|
|
||||||
|
**Theme (from theme.json)** - `header_color`, `header_font_color`, `header_height`, `navigation_color`, `navigation_font_color`, `nav_height`, `sidebar_background`, `sidebar_border`, `background_image_css`, `background_image_opacity`
|
||||||
|
|
||||||
|
**Language** - `current_lang`, `current_lang_upper`, `t_*` (translated strings)
|
||||||
|
|
||||||
|
#### Layout Options
|
||||||
|
|
||||||
|
Use YAML frontmatter to select layout:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: My Page
|
||||||
|
layout: sidebar-content
|
||||||
|
plugins: HTMLBlock
|
||||||
---
|
---
|
||||||
<h1>All Pages</h1>
|
|
||||||
<ul>
|
|
||||||
<?php foreach ($api->getAllPages() as $path => $title): ?>
|
|
||||||
<li><a href="<?= $api->buildUrl($path) ?>"><?= htmlspecialchars($title) ?></a></li>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</ul>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Analytics & Tracking
|
#### Available Layouts
|
||||||
|
|
||||||
### MQTT Tracker
|
- `sidebar-content` - Sidebar left, content right (default)
|
||||||
|
- `content` - Content only (full width)
|
||||||
|
- `sidebar` - Sidebar only
|
||||||
|
- `content-sidebar` - Content left, sidebar right
|
||||||
|
- `content-sidebar-reverse` - Content right, sidebar left
|
||||||
|
|
||||||
- Real-time page tracking via MQTT broker
|
#### Meta Data
|
||||||
- Session management
|
|
||||||
- Business Intelligence data
|
|
||||||
- Privacy aware (GDPR compliant)
|
|
||||||
- MQTT integration for dashboards
|
|
||||||
- Optional: visitor tracking, page views, performance metrics, user flows
|
|
||||||
|
|
||||||
## Frequently Asked Questions
|
```yaml
|
||||||
|
---
|
||||||
|
title: Page Title
|
||||||
|
layout: content-sidebar
|
||||||
|
description: Page description
|
||||||
|
author: Author Name
|
||||||
|
date: 2025-11-26
|
||||||
|
plugins: HTMLBlock, MQTTTracker
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
### How do I set the homepage?
|
### URL Structure
|
||||||
|
|
||||||
|
#### Frontend Page URLs
|
||||||
|
- **Home**: `/` or `/en/`
|
||||||
|
- **Page**: `/en/folder/page`
|
||||||
|
- **Search**: `?search=query` (via search form)
|
||||||
|
|
||||||
|
#### Media URLs
|
||||||
|
- **Media**: `/-media/path/to/file.jpg` (from any content subdirectory)
|
||||||
|
- **Assets**: `/-assets/file.jpg` (from content/-assets/, backward compatible)
|
||||||
|
|
||||||
|
#### Admin URLs
|
||||||
|
- **Admin**: `/admin`
|
||||||
|
- **Dashboard**: `/admin/dashboard`
|
||||||
|
- **Content**: `/admin/content`
|
||||||
|
- **Configuration**: `/admin/config`
|
||||||
|
- **Security**: `/admin/security`
|
||||||
|
- **Statistics**: `/admin/statistics`
|
||||||
|
- **Theme**: `/admin/theme`
|
||||||
|
- **Plugins**: `/admin/plugins`
|
||||||
|
- **Users**: `/admin/users`
|
||||||
|
- **Logs**: `/admin/logs`
|
||||||
|
- **Update**: `/admin/update`
|
||||||
|
- **Guide**: `/admin/guide`
|
||||||
|
|
||||||
|
### SEO Optimization
|
||||||
|
|
||||||
|
#### Meta Tags
|
||||||
|
|
||||||
|
The CMS automatically adds meta tags:
|
||||||
|
```html
|
||||||
|
<meta name="generator" content="CodePress CMS">
|
||||||
|
<meta name="author" content="E. Noorlander">
|
||||||
|
<meta name="description" content="...">
|
||||||
|
<meta name="keywords" content="...">
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Security Headers
|
||||||
|
|
||||||
|
```http
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: SAMEORIGIN
|
||||||
|
X-XSS-Protection: 1; mode=block
|
||||||
|
Referrer-Policy: strict-origin-when-cross-origin
|
||||||
|
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frequently Asked Questions
|
||||||
|
|
||||||
|
#### How do I set the homepage?
|
||||||
|
|
||||||
1. Go to **Configuration** in the admin panel (`/admin/config`)
|
1. Go to **Configuration** in the admin panel (`/admin/config`)
|
||||||
2. Select the desired page in the **Default/homepage** dropdown
|
2. Select the desired page in the **Default/homepage** dropdown
|
||||||
3. Click **Save configuration**
|
3. Click **Save configuration**
|
||||||
|
|
||||||
### How does navigation work?
|
#### How does navigation work?
|
||||||
|
|
||||||
- **Directories** become dropdown menus
|
- **Directories** become dropdown menus
|
||||||
- **Files** become direct links
|
- **Files** become direct links
|
||||||
- **Sub-directories** become nested dropdowns
|
- **Sub-directories** become nested dropdowns
|
||||||
- Only files without a language prefix show in the menu
|
- Only files without a language prefix show in the menu
|
||||||
|
|
||||||
### How do I add new content?
|
#### How do I add new content?
|
||||||
|
|
||||||
1. Via the admin panel: `/admin/content-new`
|
1. Via the admin panel: `/admin/content-new`
|
||||||
2. Or upload files to the `content/` directory
|
2. Or upload files to the `content/` directory
|
||||||
3. Organize in logical directories
|
3. Organize in logical directories
|
||||||
4. Use correct filenames and extensions
|
4. Use correct filenames and extensions
|
||||||
|
|
||||||
### How do I move a file or directory?
|
#### How do I move a file or directory?
|
||||||
|
|
||||||
1. Go to `/admin/content`
|
1. Go to `/admin/content`
|
||||||
2. Click "Move" next to the item
|
2. Click "Move" next to the item
|
||||||
3. Select the target directory
|
3. Select the target directory
|
||||||
4. Confirm the move
|
4. Confirm the move
|
||||||
|
|
||||||
### How do I create a new theme?
|
#### How do I exclude my own IP from statistics?
|
||||||
|
|
||||||
1. Go to `/admin/theme`
|
1. Go to **Configuration** in the admin panel (`/admin/config`)
|
||||||
2. Enter a name and click "Create"
|
2. Scroll to the "IP Exclusions" field
|
||||||
3. Adjust colors, heights and background
|
3. Enter your IP address (one per line)
|
||||||
4. Activate the theme
|
4. Click **Save configuration**
|
||||||
|
|
||||||
## Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
### Page not found (404)
|
#### Page not found (404)
|
||||||
|
|
||||||
1. Check filename and path
|
1. Check filename and path
|
||||||
2. Check file extension (.md, .php, .html)
|
2. Check file extension (.md, .php, .html)
|
||||||
3. Check file permissions
|
3. Check file permissions
|
||||||
4. Check if the file has the correct language prefix (`nl.` or `en.`)
|
4. Check if the file has the correct language prefix (`nl.` or `en.`)
|
||||||
|
|
||||||
### Navigation not updated
|
#### Navigation not updated
|
||||||
|
|
||||||
1. Reload the page
|
1. Reload the page
|
||||||
2. Check content directory structure
|
2. Check content directory structure
|
||||||
3. Check filenames (no spaces)
|
3. Check filenames (no spaces)
|
||||||
4. Files with language prefix only show in the correct language mode
|
4. Files with language prefix only show in the correct language mode
|
||||||
|
|
||||||
### Admin panel not accessible
|
#### Admin panel not accessible
|
||||||
|
|
||||||
1. Check if the session is still valid
|
1. Check if the session is still valid
|
||||||
2. On lockout: wait 15 minutes or clear lockout data in `admin/config/admin.json`
|
2. On lockout: wait 15 minutes or clear lockout data in `admin/config/admin.json`
|
||||||
3. Check CSRF token (reload the page)
|
3. Check CSRF token (reload the page)
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
- **CSRF protection** via tokens on all admin forms
|
|
||||||
- **Brute-force lockout** after 5 failed attempts (15 minute block)
|
|
||||||
- **Bcrypt password hashing** for user passwords
|
|
||||||
- **Path traversal prevention** via `realpath()` and prefix check
|
|
||||||
- **Direct content access** blocked (403 Forbidden)
|
|
||||||
- **Security headers** for all pages
|
|
||||||
- **PHP execution** in content directory blocked
|
|
||||||
- **File upload restrictions** on allowed extensions
|
|
||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
Current version: **1.6.0** (codename: "Enhanced")
|
Current version: **1.9.1**
|
||||||
Release date: 2026-07-14
|
Release date: 2026-07-29
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|||||||
+385
-435
File diff suppressed because it is too large
Load Diff
@@ -754,6 +754,19 @@ function handleConfig(AdminAuth $auth, array $config): void
|
|||||||
$configData['features']['search_enabled'] = !empty($_POST['feature_search']);
|
$configData['features']['search_enabled'] = !empty($_POST['feature_search']);
|
||||||
$configData['features']['breadcrumbs_enabled'] = !empty($_POST['feature_breadcrumbs']);
|
$configData['features']['breadcrumbs_enabled'] = !empty($_POST['feature_breadcrumbs']);
|
||||||
|
|
||||||
|
$parseLines = function($text) {
|
||||||
|
$lines = explode("\n", str_replace("\r", "", $text));
|
||||||
|
$clean = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$item = trim($line);
|
||||||
|
if ($item !== '') {
|
||||||
|
$clean[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array_values(array_unique($clean));
|
||||||
|
};
|
||||||
|
$configData['analytics']['excluded_ips'] = $parseLines($_POST['excluded_ips'] ?? '');
|
||||||
|
|
||||||
file_put_contents($configJson, json_encode($configData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
file_put_contents($configJson, json_encode($configData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
adminLog($config, 'info', $user['username'] . ' wijzigde site configuratie');
|
adminLog($config, 'info', $user['username'] . ' wijzigde site configuratie');
|
||||||
$message = 'Configuratie opgeslagen.';
|
$message = 'Configuratie opgeslagen.';
|
||||||
|
|||||||
+5
-2
@@ -112,7 +112,10 @@ $secSettings = $config['security'] ?? [
|
|||||||
];
|
];
|
||||||
|
|
||||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||||
$isAllowedIp = in_array($clientIp, $secSettings['allowed_ips'] ?? [], true);
|
$analyticsSettings = $config['analytics'] ?? [];
|
||||||
|
$excludedIps = $analyticsSettings['excluded_ips'] ?? [];
|
||||||
|
$isAllowedIp = in_array($clientIp, $secSettings['allowed_ips'] ?? [], true)
|
||||||
|
|| in_array($clientIp, $excludedIps, true);
|
||||||
$requestStatus = 'ok';
|
$requestStatus = 'ok';
|
||||||
|
|
||||||
if (!$isAllowedIp) {
|
if (!$isAllowedIp) {
|
||||||
@@ -182,7 +185,7 @@ if (!str_starts_with($path, '/-media/') && !str_starts_with($path, '/-assets/'))
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Record aggregated statistics
|
// Record aggregated statistics
|
||||||
if (!empty($analyticsSettings['enabled'])) {
|
if (!empty($analyticsSettings['enabled']) && !in_array($clientIp, $excludedIps, true)) {
|
||||||
$analytics = new Analytics($analyticsSettings);
|
$analytics = new Analytics($analyticsSettings);
|
||||||
$analytics->record($currentPage, $storedIp, $userAgent, $referrer, $geoCountry, $requestStatus);
|
$analytics->record($currentPage, $storedIp, $userAgent, $referrer, $geoCountry, $requestStatus);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -6,12 +6,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'version' => '1.9.1',
|
'version' => '1.9.2',
|
||||||
'release_date' => '2026-07-29',
|
'release_date' => '2026-07-29',
|
||||||
'codename' => 'Atlas',
|
'codename' => 'Atlas',
|
||||||
'status' => 'stable',
|
'status' => 'stable',
|
||||||
|
|
||||||
'changelog' => [
|
'changelog' => [
|
||||||
|
'1.9.2' => [
|
||||||
|
'date' => '2026-07-29',
|
||||||
|
'changes' => [
|
||||||
|
'Admin sidebar restructured into logical groups: Algemeen, Content, Instellingen, Gegevens, Systeem, Help, Links',
|
||||||
|
'IP uitsluitingen toegevoegd aan configuratie: IP's niet meetellen in statistieken en overslaan bij beveiligingscontroles',
|
||||||
|
'Guide (NL/EN) volledig herschreven metzelfde logische indeling als sidebar',
|
||||||
|
]
|
||||||
|
],
|
||||||
'1.9.1' => [
|
'1.9.1' => [
|
||||||
'date' => '2026-07-29',
|
'date' => '2026-07-29',
|
||||||
'changes' => [
|
'changes' => [
|
||||||
|
|||||||
Reference in New Issue
Block a user