# 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 ---

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", "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//`) ``` themes// ├── 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//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//theme.css`. #### How to create a new theme Themes are created manually: copy the `themes/default/` folder to `themes//`, 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 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 Templates are Twig files per theme in `themes//`. `ThemeManager` renders them and compiles `css/theme.scss` at runtime into `public/themes//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//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 ``` #### 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.