# 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)
│ │ │ ├── Logger.php # Structured logging system
│ │ │ ├── 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/
│ │ │ ├── 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
│ ├── templates/ # Mustache templates
│ │ ├── layout.mustache # Main layout (CSS, structure)
│ │ ├── assets/ # Header, navigation, footer partials
│ │ ├── markdown_content.mustache
│ │ ├── php_content.mustache
│ │ └── html_content.mustache
│ └── router.php # PHP dev server router
├── 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/ # Uploaded theme backgrounds
│ └── manifest.json / sw.js # PWA support
├── themes/ # Theme definitions
│ ├── default/ # Default theme
│ │ └── theme.json # Colors, heights, background
│ └── ... # Other themes
├── 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
---
All Pages
```
---
## 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",
"templates_dir": "cms\/templates",
"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`. You can create, activate, adjust colors, upload background images, and delete themes.
#### Theme Configuration (`themes//theme.json`)
```json
{
"name": "Default",
"header_color": "#0a369d",
"header_font_color": "#ffffff",
"header_height": "56",
"navigation_color": "#2754b4",
"navigation_font_color": "#ffffff",
"nav_height": "42",
"sidebar_background": "#f8f9fa",
"sidebar_border": "#dee2e6",
"background_image": "",
"background_image_opacity": "100"
}
```
#### How to create a new theme
1. Go to `/admin/theme`
2. Enter a name and click "Create"
3. Adjust colors, heights and background
4. Activate the theme
### 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 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.
---
## 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
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 ` 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
#### 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
---
```
#### Available Layouts
- `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
#### Meta Data
```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)
#### 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
```
#### 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.