Files
CodePress/guide/en.codepress.md
T
E.Noorlander a1e5baacac CMS 2.0 - Theme engine, logging, admin improvements
Major changes:
- New ThemeManager with Twig templating and SCSS compilation
- Dynamic themes system (themes/default, themes/demo)
- LogManager with SQLite storage and syslog forwarding
- RequestLogger with static helper methods
- Admin UI overhaul (Bootstrap 5, dark mode)
- Admin config page with logging and theme settings
- Admin logs page with filters and search
- Removed legacy Mustache templates
- Removed test plugin and theme
- Composer dependencies: Twig, scssphp, CommonMark, MaxMind GeoIP
2026-08-08 18:02:14 +02:00

755 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CodePress CMS Guide
## Table of Contents
- [Overview](#overview)
- [Installation](#installation)
- [Project Structure](#project-structure)
- [Content](#content)
- [Content Structure](#content-structure)
- [Content API (for PHP content files)](#content-api-for-php-content-files)
- [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)
- [URL Structure](#url-structure)
- [SEO Optimization](#seo-optimization)
- [Frequently Asked Questions](#frequently-asked-questions)
- [Troubleshooting](#troubleshooting)
- [Version](#version)
- [Support](#support)
- [License](#license)
## Overview
CodePress CMS is a lightweight, file-based content management system built with PHP (>=8.0). Works without a database.
## Installation
1. Upload files to web server
2. Set permissions for web server
3. Run `composer install` for CommonMark dependency
4. Configure `config.json` if needed
5. Access website via browser
6. **PHP development server**: `php -S localhost:8080 -t public` (uses `cms/router.php`)
## Project Structure
```
codepress/
├── cms/ # Core CMS engine
│ ├── core/
│ │ ├── class/
│ │ │ ├── CodePressCMS.php # Main CMS class (content, navigation, search)
│ │ │ ├── ThemeManager.php # Theme resolver + Twig render + SCSS compile
│ │ │ ├── Logger.php # Structured logging system
│ │ │ ├── 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/
│ │ │ ├── PluginManager.php # Plugin loader and manager
│ │ │ └── CMSAPI.php # API for plugin developers
│ │ ├── config.php # Configuration loader (merge with config.json)
│ │ └── index.php # Bootstrap (autoloader, requires)
│ ├── lang/ # Language files
│ │ ├── nl.php # Dutch translations
│ │ └── en.php # English translations
│ └── router.php # PHP dev server router (also serves /themes/)
├── themes/ # Dynamic themes (fully self-contained)
│ ├── default/ # Default theme
│ │ ├── theme.json # { title, config.default_template, template→.twig mapping }
│ │ ├── base.twig # Main layout (head, header, nav, footer)
│ │ ├── full_content.twig # Layout: full width
│ │ ├── left_sidebar.twig # Layout: sidebar left
│ │ ├── right_sidebar.twig # Layout: sidebar right
│ │ ├── custom1.twig # Layout: custom
│ │ ├── partials/ # header.twig, navigation.twig, footer.twig
│ │ ├── css/theme.scss # Colors, heights, background (compiled at runtime)
│ │ ├── js/theme.js # Theme JavaScript
│ │ └── theme.png # Preview image
│ ├── demo/ # Demo theme (same structure, different look)
│ └── test/ # Test theme
├── admin/ # Admin panel
│ ├── config/
│ │ ├── app.php # Admin app configuration (paths, timezone)
│ │ └── admin.json # Users & security (bcrypt hashes)
│ ├── src/
│ │ └── AdminAuth.php # Authentication (sessions, bcrypt, CSRF, lockout)
│ ├── templates/
│ │ ├── login.php # Login page
│ │ ├── layout.php # Admin layout with sidebar navigation
│ │ └── pages/
│ │ ├── dashboard.php # Dashboard with statistics
│ │ ├── content.php # Content overview with file upload
│ │ ├── content-edit.php # CodeMirror editor with toolbar and rename
│ │ ├── content-new.php # Create new content
│ │ ├── content-dir-form.php # Create/edit directory
│ │ ├── content-move-form.php # Move content
│ │ ├── config.php # Configuration editor
│ │ ├── security.php # Security settings
│ │ ├── statistics.php # Statistics dashboard
│ │ ├── plugins.php # Plugin overview
│ │ ├── plugins-edit.php # Plugin PHP source code editor
│ │ ├── plugins-new.php # Create new plugin
│ │ ├── plugin-config.php # Plugin configuration editor
│ │ ├── theme.php # Theme management
│ │ ├── users.php # User management
│ │ ├── logs.php # Log viewer
│ │ ├── update.php # System update
│ │ └── guide.php # Guide
│ └── storage/logs/ # Admin logs
├── cli/ # CLI scripts & tests
├── content/ # Content files
│ ├── -assets/ # Uploaded media files
│ ├── index.md # Default homepage
│ └── ... # Other content
├── plugins/ # CMS plugins
│ ├── HTMLBlock/ # Custom HTML blocks in sidebar
│ └── MQTTTracker/ # Real-time analytics and tracking
├── public/ # Web root
│ ├── index.php # Website entry point (media serving + CMS)
│ ├── admin.php # Admin entry point + routing
│ ├── .htaccess # Apache rewrite/security rules
│ ├── assets/ # CSS, JS, favicons
│ │ ├── codemirror/ # CodeMirror editor (minified JS/CSS)
│ │ └── css/js/ # Bootstrap, icons, app CSS/JS
│ ├── themes/ # Runtime compiled theme CSS (public/themes)
│ └── manifest.json / sw.js # PWA support
├── themes/ # Dynamic themes (fully self-contained)
│ ├── default/ # Default theme
│ │ ├── theme.json # Title, default template, template mapping
│ │ ├── base.twig # Main layout
│ │ ├── *.twig # Layout templates (full_content, left_sidebar, ...)
│ │ ├── partials/ # header, navigation, footer
│ │ ├── css/theme.scss # Colors, heights, background
│ │ ├── js/theme.js # Theme JavaScript
│ │ └── theme.png # Preview image
│ ├── demo/ # Demo theme
│ └── test/ # Test theme
├── config.json # Site configuration
├── version.php # Version information
└── vendor/ # Composer dependencies
```
---
## Content
### 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)
- **Language** - Default language and available languages
- **SEO** - Meta description and keywords
- **Author** - Name and website
- **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.
#### 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
{
"site_title": "CodePress",
"content_dir": "content",
"default_page": "index",
"active_theme": "default",
"language": {
"default": "en",
"available": ["nl", "en"]
},
"seo": {
"description": "CodePress CMS - Lightweight file-based content management system",
"keywords": "cms, php, content management, file-based"
},
"author": {
"name": "E. Noorlander",
"website": "https:\/\/noorlander.info"
},
"features": {
"auto_link_pages": true,
"search_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
}
}
```
### Themes
Themes are managed via the admin panel at `/admin/theme`. This is a selection page: pick the active theme and click "Activate theme". Each theme is a fully self-contained folder in `themes/` with its own Twig templates, SCSS and JavaScript.
#### Theme structure (`themes/<name>/`)
```
themes/<name>/
├── theme.json # Title, default template, template mapping
├── base.twig # Main layout (head, header, nav, footer)
├── full_content.twig # Layout: full width
├── left_sidebar.twig # Layout: sidebar left
├── right_sidebar.twig # Layout: sidebar right
├── custom1.twig # Layout: custom
├── partials/ # header.twig, navigation.twig, footer.twig
├── css/theme.scss # Colors, heights, background (compiled at runtime)
├── js/theme.js # Theme JavaScript
└── theme.png # Preview image (shown in admin)
```
#### Theme Configuration (`themes/<name>/theme.json`)
```json
{
"title": "default",
"config": {
"default_template": "full_content"
},
"template": {
"full_content": "full_content.twig",
"left_sidebar": "left_sidebar.twig",
"right_sidebar": "right_sidebar.twig",
"custom1": "custom1.twig"
}
}
```
- **`config.default_template`**: the default template used when a page requests an unknown layout.
- **`template`**: the layout-key → `.twig` file mapping. A theme can have multiple template pages.
Colors, heights and background are **not** set in `theme.json` but in `css/theme.scss`:
```scss
$header-bg: #0a369d;
$header-font: #ffffff;
$header-height: 56px;
$nav-bg: #2754b4;
$nav-font: #ffffff;
$nav-height: 42px;
$sidebar-bg: #f8f9fa;
$sidebar-border: #dee2e6;
$header-bg-image: none; // optional header background
$header-bg-opacity: 1;
```
The SCSS is compiled at runtime into `public/themes/<name>/theme.css`.
#### How to create a new theme
Themes are created manually: copy the `themes/default/` folder to `themes/<name>/`, adjust the SCSS colors and templates, and add a `theme.png` preview. The theme is then available on `/admin/theme` to activate.
### Security
Security settings are managed via `/admin/security`. Includes:
#### Bot, AI & Scraper Blocking
Incoming requests are checked against known bot and AI crawler patterns via the User-Agent header. Detected bots receive a **403 Forbidden** response.
| Category | Examples |
|---|---|
| AI Crawlers | GPTBot, ChatGPT-User, Claude-Web, ClaudeBot, Google-Extended, CCBot, PerplexityBot |
| Search Engines | Googlebot, Bingbot, BingPreview, DuckDuckBot, YandexBot, Baiduspider |
| Scrapers | HTTrack, Scrapy, PhantomJS |
#### Rate Limiting
Prevents IPs from overloading the site. Returns HTTP 429 on exceedance.
#### IP Lists
- **IP Whitelist** - IPs on the whitelist are never blocked
- **IP Blocklist** - IPs on the blocklist always receive a 403 Forbidden
#### Dynamic robots.txt
The system automatically generates a `robots.txt` based on your security settings, available at `/robots.txt`.
---
## Data
### 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 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.
- **Dynamic log** — structured log entries via `LogManager`, with event type, level, IP, and message.
#### Configuring dynamic logging
Via `/admin/config`**Logging** you can configure how and what is recorded:
- **Storage**: `SQLite` (default) or `Syslog`.
- **Syslog server**: if a host is provided, log entries are sent to that server over UDP. Leave empty to use SQLite.
- **Facility**: the category of the log source in syslog. `local0``local7` are for your own applications; `daemon`, `user`, and `auth` are standard system categories.
- **Syslog ident**: the name that appears in the log message (e.g. `codepress`).
- **Events**: choose which types are recorded — `admin`, `requests`, `errors`, `security`, `content`, `system`.
If no syslog server is configured, SQLite is always used (with a file fallback if SQLite is unavailable).
The dashboard shows the last 20 entries of each log. Click "View all →" for the full list, where you can also download or clear.
---
## System
### Plugin System
#### Plugin Structure
```
plugins/
├── HTMLBlock/
│ ├── HTMLBlock.php # Plugin class (required)
│ ├── config.json # Configuration (optional)
│ └── README.md # Documentation (optional)
├── MQTTTracker/
│ ├── MQTTTracker.php
│ ├── config.json
│ └── README.md
```
#### Plugin Development
- **API access** via `CMSAPI` class - gives access to CMS configuration, templates, menu
- **Sidebar content** with `getSidebarContent()` - returns HTML for sidebar
- **Metadata access** from YAML frontmatter via `CMSAPI`
- **Configuration** via `config.json` - editable through admin panel
- **viewable** field in config.json determines if plugin is visible in sidebar
- **Per-page visibility** - via the editor plugin selector per page
#### Plugin Boilerplate
```php
<?php
class MyPlugin
{
private ?CMSAPI $api = null;
private array $config;
public function __construct()
{
$this->config = [
'viewable' => true,
];
}
public function setAPI(CMSAPI $api): void
{
$this->api = $api;
}
public function getSidebarContent(): string
{
return '';
}
public function getConfig(): array
{
return $this->config;
}
public function setConfig(array $config): void
{
$this->config = array_merge($this->config, $config);
}
}
```
#### 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.
### User Management
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)
Passwords are stored as bcrypt hashes in `admin/config/admin.json`.
### Update
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.
---
## Guide (in Admin)
This guide is also built into the admin panel via `/admin/guide`, with support for Dutch and English.
---
## Other
### Templates
Templates are Twig files per theme in `themes/<name>/`. `ThemeManager` renders them and compiles `css/theme.scss` at runtime into `public/themes/<name>/theme.css`.
#### 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** - `theme_title`, `theme_css_url`, `theme_js_url`, `theme_config` (config from theme.json)
**Language** - `current_lang`, `current_lang_upper`, `t_*` (translated strings)
#### Layout Options
Use YAML frontmatter to select the template. The layout key references a template in the active theme:
```yaml
---
title: My Page
layout: left_sidebar
plugins: HTMLBlock
---
```
#### Available Layouts
The available layouts are defined by the `template` section of the active theme (`themes/<name>/theme.json`). The default theme includes:
- `full_content` - Content only (full width)
- `left_sidebar` - Sidebar left, content right
- `right_sidebar` - Content left, sidebar right
- `custom1` - Custom layout
If a page requests an unknown layout, the `default_template` from the theme's `config` is used.
#### Meta Data
```yaml
---
title: Page Title
layout: left_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)
#### 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`)
2. Select the desired page in the **Default/homepage** dropdown
3. Click **Save configuration**
#### How does navigation work?
- **Directories** become dropdown menus
- **Files** become direct links
- **Sub-directories** become nested dropdowns
- Only files without a language prefix show in the menu
#### How do I add new content?
1. Via the admin panel: `/admin/content-new`
2. Or upload files to the `content/` directory
3. Organize in logical directories
4. Use correct filenames and extensions
#### How do I move a file or directory?
1. Go to `/admin/content`
2. Click "Move" next to the item
3. Select the target directory
4. Confirm the move
#### How do I exclude my own IP from statistics?
1. Go to **Configuration** in the admin panel (`/admin/config`)
2. Scroll to the "IP Exclusions" field
3. Enter your IP address (one per line)
4. Click **Save configuration**
### Troubleshooting
#### Page not found (404)
1. Check filename and path
2. Check file extension (.md, .php, .html)
3. Check file permissions
4. Check if the file has the correct language prefix (`nl.` or `en.`)
#### Navigation not updated
1. Reload the page
2. Check content directory structure
3. Check filenames (no spaces)
4. Files with language prefix only show in the correct language mode
#### Admin panel not accessible
1. Check if the session is still valid
2. On lockout: wait 15 minutes or clear lockout data in `admin/config/admin.json`
3. Check CSRF token (reload the page)
## Version
Current version: **1.9.1**
Release date: 2026-07-29
## Support
For technical support:
- **Git**: https://git.noorlander.info/E.Noorlander/CodePress
- **Website**: https://noorlander.info
- **Issues**: Report problems via Git issues
## License
CodePress CMS is open-source software under dual-license: AGPL v3 for open-source use, commercial license for proprietary use.