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

24 KiB
Raw Blame History

CodePress CMS Guide

Table of Contents

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

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

// Get menu structure
$menu = $api->getMenu();
// Nested array with 'title', 'path', 'url', 'children'

Configuration

// Get config value (dot notation)
$title = $api->getConfig('site_title');
$lang = $api->getConfig('language.default');
$seoDesc = $api->getConfig('seo.description', 'Default description');

Current page

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

// 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();
// Get translation
$label = $api->t('home');

// Search results (if searching)
if ($api->isSearching()) {
    $results = $api->getSearchResults();
}

Example PHP content file

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

{
    "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)

{
    "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:

$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/configLogging 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. local0local7 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

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:

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

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

<meta name="generator" content="CodePress CMS">
<meta name="author" content="E. Noorlander">
<meta name="description" content="...">
<meta name="keywords" content="...">

Security Headers

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:

License

CodePress CMS is open-source software under dual-license: AGPL v3 for open-source use, commercial license for proprietary use.