Merge development v2.6.0 into main
Resolved conflicts by taking development (v2.6.0) version for all files. Removed statistics.twig (replaced by Statistics plugin).
This commit is contained in:
@@ -11,5 +11,7 @@ The admin manager guide contains the following topics:
|
||||
- **Users** - User management with roles (Admin, Content Manager, BI Manager, Site Admin)
|
||||
- **Statistics** - Viewing and exporting visitor statistics
|
||||
- **Logs** - Viewing and filtering log files
|
||||
- **Media** - Managing media files
|
||||
- **Update** - Check for updates
|
||||
|
||||
Select a topic from the navigation on the left.
|
||||
@@ -3,7 +3,13 @@
|
||||
## General settings
|
||||
|
||||
- **Site title** - Website name
|
||||
- **Default language** - nl/en/de/fr
|
||||
- **Author** - Name and email
|
||||
- **Analytics** - Visitor tracking on/off
|
||||
- **Logging** - Log files on/off
|
||||
- **Admin language** - Admin panel language (nl/en/de)
|
||||
- **Content language** - Default website content language (nl/en/de/fr)
|
||||
|
||||
## Homepage
|
||||
|
||||
Choose which page is shown on the homepage:
|
||||
|
||||
- **Automatic** - First available page
|
||||
- **Most recent** - Last modified page
|
||||
- **Specific page** - Select a specific page from the content directory
|
||||
@@ -2,48 +2,29 @@
|
||||
|
||||
## Plugin types
|
||||
|
||||
CodePress has two kinds of plugins, determined by the `type` field in `plugin.json`:
|
||||
There are two types of plugins:
|
||||
|
||||
- **Content plugins** (`type: "content"`) — Appear in the sidebar and in the plugin selection on content-edit pages. Provide sidebar content via `getSidebarContent()`.
|
||||
- **System plugins** (`type: "system"`) — Are loaded by PluginManager but do NOT appear in the sidebar. Provide functionality via admin menu, routes and the CMSAPI. Registered via `getAdminMenu()` and `getAdminRoutes()`.
|
||||
|
||||
When no `type` field is present, `content` is assumed.
|
||||
- **System plugins** (blue badge) - Manage CMS functionality such as Dashboard, Logs and Statistics
|
||||
- **Content plugins** (green badge) - Display content on the front-end such as HTMLBlock and Navigation
|
||||
|
||||
## Essential plugins
|
||||
|
||||
Essential plugins are defined in `getProtectedPlugins()` in `public/admin.php`. Current essential plugin: **Navigation**.
|
||||
|
||||
These plugins:
|
||||
- Cannot be deactivated
|
||||
- Cannot be edited
|
||||
- Cannot be deleted
|
||||
|
||||
On the plugins page they get an **Essential** badge instead of the action buttons.
|
||||
Essential plugins (with a shield icon) cannot be edited, deactivated, or deleted. This applies to the Navigation plugin for example.
|
||||
|
||||
## Managing plugins
|
||||
|
||||
- **Activate/Deactivate** — Turn a plugin on/off (not for essential plugins)
|
||||
- **Edit** — Modify plugin code in CodeMirror (not for essential plugins)
|
||||
- **Configuration** — Edit plugin settings
|
||||
- **Delete** — Remove the plugin folder (not for essential plugins)
|
||||
- **Enable/Disable** - Turn plugins on/off
|
||||
- **Edit** - Modify plugin code
|
||||
- **Configuration** - Plugin settings (only for plugins with a Config button)
|
||||
- **Delete** - Remove plugin
|
||||
|
||||
The admin plugins page shows a **Content** or **System** badge per plugin.
|
||||
Only active plugins are shown in the admin sidebar.
|
||||
|
||||
## Creating a new plugin
|
||||
## New plugin
|
||||
|
||||
1. Go to **Plugins** → **New plugin**
|
||||
2. Enter a name (e.g. `MyPlugin`) — this becomes the folder name
|
||||
3. Choose the type: **Content** or **System**
|
||||
4. The file `MyPlugin.php` is created (NOT `plugin.php`)
|
||||
5. Edit the plugin code in CodeMirror
|
||||
6. Activate the plugin
|
||||
2. Enter a name (e.g. `MyPlugin`)
|
||||
3. Edit the PHP file
|
||||
4. Enable the plugin
|
||||
|
||||
## Plugin filename
|
||||
|
||||
Plugin PHP files are named `<PluginName>.php` (e.g. `Navigation.php`, `HTMLBlock.php`). `PluginManager` loads `$pluginDir . '/' . $pluginName . '.php'`.
|
||||
|
||||
## Plugin CSS
|
||||
|
||||
- Content and system plugins can have their own CSS in `assets/scss/` and `assets/css/`
|
||||
- Plugin CSS is automatically loaded after theme CSS (in `base.twig`), so themes can override plugin styling
|
||||
- Plugin assets are served via `cms/router.php` at URL `/plugins/<Name>/assets/...`
|
||||
New plugins are content-plugins by default. To create a system plugin, add `"type": "system"` to `plugin.json`.
|
||||
@@ -1,84 +1,24 @@
|
||||
# CMS Architecture
|
||||
|
||||
CodePress is a file-based CMS without a database. Content, configuration and users are stored in files.
|
||||
|
||||
## Folder structure
|
||||
|
||||
```
|
||||
codepress/
|
||||
├── cms/ # Core CMS engine
|
||||
│ ├── core/
|
||||
│ │ ├── class/
|
||||
│ │ │ ├── CodePressCMS.php # Main CMS class (routing, rendering, breadcrumb)
|
||||
│ │ │ ├── ThemeManager.php # Theme resolver + Twig render + SCSS compile
|
||||
│ │ │ ├── ContentAPI.php # Read-only API for PHP content
|
||||
│ │ │ ├── ContentSecurityPolicy.php # CSP header management
|
||||
│ │ │ ├── Analytics.php # Visitor statistics
|
||||
│ │ │ ├── BotGuard.php # Bot/AI detection
|
||||
│ │ │ ├── Cache.php # Cache system
|
||||
│ │ │ ├── GeoIP.php # GeoIP lookup (country, flag)
|
||||
│ │ │ ├── Logger.php # Basic logging
|
||||
│ │ │ ├── LogManager.php # Dynamic logging (SQLite/syslog)
|
||||
│ │ │ ├── RateLimiter.php # Rate limiting per IP
|
||||
│ │ │ ├── RequestLogger.php # Request logging + visitor info
|
||||
│ │ │ ├── SearchEngine.php # Full text search
|
||||
│ │ │ └── AccessibilityManager.php # Accessibility features
|
||||
│ │ ├── plugin/
|
||||
│ │ │ ├── PluginManager.php # Plugin loader (hooks, filters, sidebar, admin routes)
|
||||
│ │ │ └── CMSAPI.php # API for plugins (getPage, getConfig, etc.)
|
||||
│ │ ├── config.php # Config loader (reads config.json)
|
||||
│ │ └── index.php # Bootstrap (autoloader, requires)
|
||||
│ ├── lang/ # Language files (nl.php, en.php)
|
||||
│ └── router.php # PHP dev server router
|
||||
├── themes/ # Dynamic themes
|
||||
│ └── default/ # Default theme (views + assets)
|
||||
│ ├── theme.json # { title, config.default_template, template: layout→.twig }
|
||||
│ ├── base.twig # Main layout
|
||||
│ ├── *.twig # Layout templates
|
||||
│ ├── partials/ # header.twig, navigation.twig, footer.twig
|
||||
│ └── assets/
|
||||
│ ├── scss/theme.scss # SCSS source (only CSS source)
|
||||
│ ├── css_compiled/ # Generated by scssphp (read-only)
|
||||
│ ├── css/ # External CSS (bootstrap.min.css, etc.)
|
||||
│ ├── js/ # JavaScript
|
||||
│ └── img/ # Images
|
||||
├── admin/ # Admin panel
|
||||
│ ├── config/
|
||||
│ │ ├── app.php # Admin app configuration
|
||||
│ │ └── admin.json # Users & security (file-based, .gitignore'd)
|
||||
│ ├── src/
|
||||
│ │ └── AdminAuth.php # Authentication + RBAC
|
||||
│ ├── theme/default/ # Admin theme (views + assets)
|
||||
│ │ ├── theme.json
|
||||
│ │ ├── assets/ # CSS, JS, CodeMirror, fonts
|
||||
│ │ └── views/ # login.twig, layouts/, pages/
|
||||
│ └── storage/ # Logs, cache, geoip
|
||||
├── plugins/ # CMS plugins
|
||||
│ ├── Navigation/ # Essential navigation plugin (protected)
|
||||
│ │ ├── Navigation.php # Plugin code (NOT plugin.php)
|
||||
│ │ ├── plugin.json # Metadata with type field
|
||||
│ │ └── assets/ # SCSS + CSS
|
||||
│ └── HTMLBlock/ # Example sidebar plugin
|
||||
├── content/ # Website content (.md, .php, .html) — .gitignore'd
|
||||
├── guide/ # Guides (nl/en)
|
||||
├── public/ # Web root
|
||||
│ ├── index.php # Website entry point
|
||||
│ ├── admin.php # Admin entry point + routing
|
||||
│ ├── asset.php # Asset server for Apache (themes/, admin/assets/, plugins/)
|
||||
│ ├── .htaccess # Forwards asset URLs to asset.php
|
||||
│ ├── favicon.ico
|
||||
│ └── robots.txt
|
||||
├── var/ # Cache (twig) — .gitignore'd
|
||||
├── config.json # Site configuration — .gitignore'd
|
||||
├── composer.json # PHP dependencies (CommonMark, Twig, scssphp)
|
||||
└── version.php # Version information (2.5.2)
|
||||
```
|
||||
|
||||
## Principles
|
||||
|
||||
- **No database** — Everything in files (content in `content/`, users in `admin/config/admin.json`, config in `config.json`)
|
||||
- **File-based auth** — `AdminAuth` uses bcrypt hashes in `admin.json`, sessions for login
|
||||
- **Twig templating** — Themes use Twig; `ThemeManager` renders via Twig
|
||||
- **SCSS compilation** — `assets/scss/theme.scss` → `assets/css_compiled/theme.css` via scssphp (read-only output)
|
||||
- **Plugin system** — `PluginManager` loads content and system plugins with hooks/filters
|
||||
- **Asset serving** — PHP dev router (`cms/router.php`) or Apache (`.htaccess` → `public/asset.php`)
|
||||
├── cms/core/ # Core engine
|
||||
│ ├── class/ # CodePressCMS, ThemeManager, Logger, Analytics
|
||||
│ ├── plugin/ # PluginManager, CMSAPI, AdminPluginAPI
|
||||
│ ├── config.php # Config loader
|
||||
│ └── index.php # Bootstrap
|
||||
├── admin/ # Admin console
|
||||
│ ├── config/ # app.php, admin.json
|
||||
│ ├── src/ # AdminAuth
|
||||
│ └── theme/default/views/ # Twig templates
|
||||
├── themes/ # Themes (default, demo, ...)
|
||||
├── plugins/ # Plugins (Dashboard, HTMLBlock, Navigation, ...)
|
||||
├── content/ # Content files (.md, .php, .html)
|
||||
├── language/ # Language files (nl/, en/, de/)
|
||||
├── guide/ # Guides (nl/, en/)
|
||||
├── public/ # Web root
|
||||
│ ├── index.php # Website entry point
|
||||
│ └── admin.php # Admin entry point + router
|
||||
├── config.json # Site configuration
|
||||
└── version.php # Version info
|
||||
```
|
||||
@@ -5,17 +5,11 @@
|
||||
Main CMS class in `cms/core/class/CodePressCMS.php`:
|
||||
|
||||
```php
|
||||
$cms = new CodePressCMS();
|
||||
$cms->init();
|
||||
$cms = new CodePressCMS($config);
|
||||
$cms->renderPage($pagePath);
|
||||
```
|
||||
|
||||
Responsibilities:
|
||||
- Routing and page rendering
|
||||
- Breadcrumb generation (dynamic: Home > [subfolders] > [page])
|
||||
- Loading guide pages (`getGuidePage()`)
|
||||
- Title extraction from H1 (`getTitleFromFile()`)
|
||||
- Display name processing (`formatDisplayName()`)
|
||||
Key methods: `renderPage()`, `getMenu()`, `getPage()`, `parseMarkdown()`, `generateBreadcrumb()`, `getAvailableLanguages()`.
|
||||
|
||||
## ThemeManager.php
|
||||
|
||||
@@ -23,20 +17,10 @@ Theme management in `cms/core/class/ThemeManager.php`:
|
||||
|
||||
```php
|
||||
$themeManager = new ThemeManager($config);
|
||||
$themeManager->getActiveTheme();
|
||||
$themeManager->renderTwig($template, $data);
|
||||
$themeManager->compileCss($force);
|
||||
$themeManager->getCssUrl();
|
||||
```
|
||||
|
||||
Responsibilities:
|
||||
- Resolving the active theme from `config.json`
|
||||
- Loading `theme.json` (title, config.default_template, template mapping)
|
||||
- Building the Twig environment (rooted in the theme folder)
|
||||
- **SCSS compilation** — compiles `assets/scss/theme.scss` to `assets/css_compiled/theme.css` via scssphp
|
||||
- Resolving the layout to a `.twig` template (fallback to `config.default_template`)
|
||||
|
||||
`getCssUrl()` priority: 1) `assets/css/theme.css` (manual), 2) `assets/css_compiled/theme.css` (compiled).
|
||||
Compiles `assets/scss/theme.scss` at runtime to CSS and renders Twig templates.
|
||||
|
||||
## PluginManager.php
|
||||
|
||||
@@ -44,66 +28,39 @@ Plugin system in `cms/core/plugin/PluginManager.php`:
|
||||
|
||||
```php
|
||||
$pluginManager = new PluginManager($pluginsPath, $enabledPlugins);
|
||||
$pluginManager->getSidebarContent($allowedPlugins);
|
||||
$pluginManager->getPluginCssUrls();
|
||||
$pluginManager->executeHook($name, $params);
|
||||
$pluginManager->setAPI($api);
|
||||
$pluginManager->doAction('onPageLoad', $args);
|
||||
$result = $pluginManager->applyFilters('onContentFilter', $content);
|
||||
$pluginManager->getAdminMenuItems();
|
||||
$pluginManager->handleAdminRoute($route);
|
||||
$pluginManager->getPluginType($pluginName);
|
||||
```
|
||||
|
||||
Responsibilities:
|
||||
- Loading plugins from `plugins/` folders (only enabled plugins)
|
||||
- Plugin file: `<PluginName>.php` (NOT `plugin.php`)
|
||||
- Hooks and filters system (`doAction`, `applyFilters`)
|
||||
- Collecting sidebar content from content plugins (`getSidebarContent`)
|
||||
- Collecting plugin CSS URLs (`getPluginCssUrls`)
|
||||
- **Admin menu items** from system plugins (`getAdminMenuItems`)
|
||||
- **Admin routes** handled via system plugins (`handleAdminRoute`)
|
||||
- Determining the **plugin type** (`getPluginType` — `content` or `system`)
|
||||
- `isPluginViewable` — system plugins do not appear in the sidebar
|
||||
|
||||
## AdminAuth.php
|
||||
|
||||
Authentication and RBAC in `admin/src/AdminAuth.php`:
|
||||
|
||||
```php
|
||||
$auth = new AdminAuth($appConfig);
|
||||
$auth->login($username, $password);
|
||||
$auth->logout();
|
||||
$auth->hasPermission($route);
|
||||
$auth->verifyCsrf($token);
|
||||
```
|
||||
|
||||
Constants:
|
||||
- `ROLE_PERMISSIONS` — Mapping of role → allowed route prefixes. `admin` has wildcard `*`.
|
||||
- `ROLE_LABELS` — Human-readable labels per role.
|
||||
|
||||
Roles:
|
||||
|
||||
| Role | Label | Permissions |
|
||||
|------|-------|-------------|
|
||||
| `admin` | Admin | Everything (`*`) |
|
||||
| `content-manager` | Content Manager | Content management, guide |
|
||||
| `bi-manager` | BI Manager | Statistics, logs, guide |
|
||||
| `site-admin` | Site Admin | Theme, plugins, statistics, logs, update, guide |
|
||||
|
||||
Responsibilities:
|
||||
- Session-based authentication
|
||||
- bcrypt password hashing
|
||||
- CSRF tokens (`verifyCsrf`)
|
||||
- Brute-force lockout (login attempts tracking)
|
||||
- RBAC via `hasPermission($route)` — checks the route against `ROLE_PERMISSIONS`
|
||||
Key methods: `setAPI()`, `doAction()`, `applyFilters()`, `getPlugin()`, `getAllPlugins()`, `getEnabledPlugins()`, `isEnabled()`, `getSidebarContent()`, `getPluginCssUrls()`, `getAdminMenuItems()`, `dispatchAdminRoute()`, `resolveAdminRoute()`.
|
||||
|
||||
## CMSAPI.php
|
||||
|
||||
Plugin API in `cms/core/plugin/CMSAPI.php`:
|
||||
Front-end plugin API in `cms/core/plugin/CMSAPI.php`. Implements `PluginAPIInterface`. Provides access to the CMS instance for plugins in the front-end context.
|
||||
|
||||
```php
|
||||
$api = PluginManager::getAPI();
|
||||
$config = $api->getConfig();
|
||||
$page = $api->getPage($path);
|
||||
$content = $api->getContent();
|
||||
$api->getCurrentPageTitle();
|
||||
$api->getMenu();
|
||||
$api->getConfig('site_title');
|
||||
$api->createUrl('about-us');
|
||||
```
|
||||
|
||||
Provides read-only access for plugins to CMS data (config, pages, content).
|
||||
## AdminPluginAPI.php
|
||||
|
||||
Admin plugin API in `cms/core/plugin/AdminPluginAPI.php`. Implements `PluginAPIInterface`. Lightweight wrapper with config-only access (no CMS instance needed).
|
||||
|
||||
```php
|
||||
$api->getConfig('analytics.enabled');
|
||||
$api->getContentDir();
|
||||
$api->getEnabledPlugins();
|
||||
```
|
||||
|
||||
## AdminAuth.php
|
||||
|
||||
Authentication in `admin/src/AdminAuth.php`. Manages sessions, bcrypt passwords, CSRF tokens, brute-force lockout, and role-based permissions.
|
||||
|
||||
## LogManager.php
|
||||
|
||||
Logging system in `cms/core/class/LogManager.php`. Supports SQLite, syslog, and file-based logging. Events: admin, requests, errors, security, content, system.
|
||||
@@ -1,144 +1,146 @@
|
||||
# Plugin Development
|
||||
|
||||
## Plugin types
|
||||
|
||||
CodePress has two kinds of plugins, determined by the `type` field in `plugin.json`:
|
||||
|
||||
- **Content plugins** (`type: "content"`) — Appear in the sidebar and in the plugin selection on content-edit pages. Provide sidebar content via `getSidebarContent()`.
|
||||
- **System plugins** (`type: "system"`) — Are loaded by PluginManager but do NOT appear in the sidebar. Provide functionality via admin menu, routes and the CMSAPI.
|
||||
|
||||
When no `type` field is present, `content` is assumed.
|
||||
|
||||
## Plugin structure
|
||||
|
||||
```
|
||||
plugins/MyPlugin/
|
||||
├── MyPlugin.php # Plugin code (NOT plugin.php)
|
||||
├── plugin.json # Metadata with type field
|
||||
├── config.json # Optional configuration
|
||||
└── assets/
|
||||
├── scss/myplugin.scss # SCSS source (optional)
|
||||
└── css/myplugin.css # CSS (manually maintained)
|
||||
├── MyPlugin.php # Main plugin class (name = folder name)
|
||||
├── plugin.json # Plugin metadata
|
||||
├── config.json # Optional configuration
|
||||
└── assets/ # Optional CSS/JS
|
||||
├── css/
|
||||
└── scss/
|
||||
```
|
||||
|
||||
Plugin files are named `<PluginName>.php` (e.g. `Navigation.php`, `HTMLBlock.php`). `PluginManager` loads `$pluginDir . '/' . $pluginName . '.php'`.
|
||||
|
||||
## plugin.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "My Plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "Your Name",
|
||||
"description": "Description",
|
||||
"type": "content"
|
||||
"name": "My Plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "Your Name",
|
||||
"description": "Description",
|
||||
"type": "content",
|
||||
"essential": false,
|
||||
"hasConfig": false
|
||||
}
|
||||
```
|
||||
|
||||
- `name` — Display name
|
||||
- `type` — `"content"` or `"system"` (default: `content`)
|
||||
### Fields
|
||||
|
||||
## Content plugin example
|
||||
| Field | Value | Description |
|
||||
|-------|-------|-------------|
|
||||
| `name` | string | Display name in admin |
|
||||
| `version` | string | Version number |
|
||||
| `author` | string | Author |
|
||||
| `description` | string | Short description |
|
||||
| `type` | `"system"` or `"content"` | System (blue badge) or content (green badge) |
|
||||
| `essential` | boolean | Essential plugins cannot be edited/deleted |
|
||||
| `hasConfig` | boolean | Shows a Config button in admin |
|
||||
|
||||
## Plugin class example
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Plugin: MyPlugin (content)
|
||||
*/
|
||||
|
||||
class MyPlugin
|
||||
{
|
||||
private ?PluginAPIInterface $api = null;
|
||||
|
||||
public function setAPI(PluginAPIInterface $api): void
|
||||
{
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
public function getConfig(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'My Plugin',
|
||||
'type' => 'content',
|
||||
'viewable' => true,
|
||||
];
|
||||
}
|
||||
|
||||
public function getSidebarContent(): string
|
||||
{
|
||||
return '<p>Hello World</p>';
|
||||
$title = $this->api ? $this->api->getCurrentPageTitle() : '';
|
||||
return '<p>Current page: ' . htmlspecialchars($title) . '</p>';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## System plugin example
|
||||
The plugin class is automatically loaded by `PluginManager` when the plugin is listed in `enabled_plugins` in `config.json`.
|
||||
|
||||
## Using the API
|
||||
|
||||
The API is injected via `setAPI()`, not via a static method. In the front-end context this is a `CMSAPI` instance, in the admin context an `AdminPluginAPI` instance. Both implement `PluginAPIInterface`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Plugin: MySystem (system)
|
||||
*/
|
||||
class MySystem
|
||||
// Front-end API (CMSAPI)
|
||||
$this->api->getCurrentPageTitle();
|
||||
$this->api->getMenu();
|
||||
$this->api->getConfig('site_title');
|
||||
$this->api->getCurrentLanguage();
|
||||
$this->api->isHomepage();
|
||||
$this->api->createUrl('about-us');
|
||||
|
||||
// Admin API (AdminPluginAPI)
|
||||
$this->api->getConfig('analytics.enabled');
|
||||
$this->api->getContentDir();
|
||||
$this->api->getEnabledPlugins();
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
Plugins can implement the following methods for automatic hook registration:
|
||||
|
||||
**Actions** (no return value):
|
||||
|
||||
- `onPageLoad` - On page load
|
||||
- `onBeforeRender` - Before rendering
|
||||
- `onAfterRender` - After rendering
|
||||
- `onSearch` - On search
|
||||
- `onMenuBuild` - On menu build
|
||||
|
||||
**Filters** (return modified value):
|
||||
|
||||
- `onContentFilter` - Filter content
|
||||
- `onTitleFilter` - Filter title
|
||||
- `onMenuFilter` - Filter menu
|
||||
|
||||
## Admin integration
|
||||
|
||||
Plugins can add custom admin pages via `getAdminMenu()` and `handleAdminRoute()`:
|
||||
|
||||
```php
|
||||
public function getAdminMenu(): array
|
||||
{
|
||||
public function getConfig(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'My System',
|
||||
'type' => 'system',
|
||||
];
|
||||
}
|
||||
return [
|
||||
[
|
||||
'plugin' => 'MyPlugin',
|
||||
'route' => 'my-plugin',
|
||||
'label' => 'My Plugin',
|
||||
'icon' => 'bi-puzzle',
|
||||
'section' => 'general', // or 'system'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getAdminMenu(): array
|
||||
{
|
||||
return [
|
||||
['label' => 'My System', 'route' => 'my-system', 'icon' => 'bi-gear'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getAdminRoutes(): array
|
||||
{
|
||||
return ['my-system'];
|
||||
}
|
||||
|
||||
public function handleAdminRoute(string $route): void
|
||||
{
|
||||
echo '<h1>My System page</h1>';
|
||||
}
|
||||
public function handleAdminRoute(string $action): ?string
|
||||
{
|
||||
return '<h2>My Plugin admin page</h2>';
|
||||
}
|
||||
```
|
||||
|
||||
System plugins are shown in the admin sidebar via `PluginManager::getAdminMenuItems()` and handled via `PluginManager::handleAdminRoute()`.
|
||||
Only plugins listed in `enabled_plugins` are shown in the admin sidebar.
|
||||
|
||||
## Using CMSAPI
|
||||
## Adding CSS
|
||||
|
||||
```php
|
||||
<?php
|
||||
$api = PluginManager::getAPI();
|
||||
$config = $api->getConfig();
|
||||
$page = $api->getPage($path);
|
||||
```
|
||||
|
||||
`setAPI()` is called automatically by `PluginManager` if the plugin has the method.
|
||||
|
||||
## Plugin CSS
|
||||
|
||||
- Plugin CSS in `assets/css/` is manually maintained (SCSS compilation for plugins is not yet automatic)
|
||||
- Plugin CSS is automatically loaded after theme CSS (in `base.twig`), so themes can override plugin styling
|
||||
- Plugin assets are served via `cms/router.php` at URL `/plugins/<Name>/assets/...`
|
||||
- Implement `getCssUrl()` in the plugin class to return the CSS URL
|
||||
Plugins can provide a CSS URL via `getCssUrl()`:
|
||||
|
||||
```php
|
||||
public function getCssUrl(): string
|
||||
{
|
||||
return '/plugins/MyPlugin/assets/css/myplugin.css';
|
||||
return '/plugins/MyPlugin/assets/css/style.css';
|
||||
}
|
||||
```
|
||||
|
||||
## Hooks and filters
|
||||
|
||||
PluginManager auto-registers these methods as hooks/filters:
|
||||
|
||||
- **Hooks**: `onPageLoad`, `onBeforeRender`, `onAfterRender`, `onSearch`, `onMenuBuild`
|
||||
- **Filters**: `onContentFilter`, `onTitleFilter`, `onMenuFilter`
|
||||
|
||||
```php
|
||||
public function onPageLoad($page) { /* ... */ }
|
||||
public function onContentFilter($content) { return $content; }
|
||||
```
|
||||
|
||||
## Essential plugins
|
||||
|
||||
Essential plugins are defined in `getProtectedPlugins()` in `public/admin.php`. Current essential plugin: **Navigation**.
|
||||
|
||||
These plugins cannot be deactivated, edited or deleted. Always add the `isProtectedPlugin()` check to new plugin handlers.
|
||||
```
|
||||
@@ -1,7 +1,5 @@
|
||||
# Layouts
|
||||
|
||||
Layouts define the page structure (left sidebar, full width, etc.). Layouts are defined in `theme.json` and chosen via frontmatter in content files.
|
||||
|
||||
## Choosing a layout in content
|
||||
|
||||
```markdown
|
||||
@@ -16,58 +14,19 @@ Content...
|
||||
|
||||
## Layouts in theme.json
|
||||
|
||||
Layouts are defined in the `template` mapping. The `config.default_template` is the fallback for pages without a `layout:` frontmatter or with an unknown layout:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"default_template": "full_content"
|
||||
},
|
||||
"template": {
|
||||
"full_content": "full_content.twig",
|
||||
"left_sidebar": "left_sidebar.twig",
|
||||
"right_sidebar": "right_sidebar.twig",
|
||||
"custom1": "custom1.twig",
|
||||
"guide": "guide.twig"
|
||||
}
|
||||
"config": {
|
||||
"default_template": "full_content"
|
||||
},
|
||||
"template": {
|
||||
"full_content": "full_content.twig",
|
||||
"left_sidebar": "left_sidebar.twig",
|
||||
"right_sidebar": "right_sidebar.twig",
|
||||
"custom1": "custom1.twig",
|
||||
"guide": "guide.twig"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Layout template
|
||||
|
||||
A layout template extends `base.twig` and fills the `content` block:
|
||||
|
||||
```twig
|
||||
{% extends 'base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{{ content|raw }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
## Layouts with sidebar
|
||||
|
||||
Layouts with a sidebar can show plugin content:
|
||||
|
||||
```twig
|
||||
{% extends 'base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<main class="col-md-8">
|
||||
{{ content|raw }}
|
||||
</main>
|
||||
<aside class="col-md-4">
|
||||
{{ sidebar_content|raw }}
|
||||
</aside>
|
||||
</div>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
If a page has no plugins or the layout has no sidebar, `sidebar_content` is empty.
|
||||
|
||||
## Guide layout
|
||||
|
||||
The `guide` layout (`guide.twig`) is special for guides. It injects the Navigation plugin into the sidebar for sidebar navigation. Automatically used for pages in the `guide/` folder.
|
||||
Unknown layouts in frontmatter fall back to `config.default_template`.
|
||||
@@ -22,4 +22,5 @@ Create basic files:
|
||||
- `full_content.twig`
|
||||
- `partials/header.twig`
|
||||
- `partials/footer.twig`
|
||||
- `scss/theme.scss`
|
||||
- `scss/theme.scss`
|
||||
- `guide.twig` (if guide support is needed)
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
1. Go to **Admin** → **Theme**
|
||||
2. Click **Compile SCSS** for your theme
|
||||
3. CSS is generated in `assets/css/theme.css`
|
||||
3. CSS is generated in `assets/css_compiled/theme.css`
|
||||
|
||||
## SCSS example
|
||||
|
||||
|
||||
@@ -2,45 +2,24 @@
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "My Theme",
|
||||
"config": {
|
||||
"default_template": "full_content"
|
||||
},
|
||||
"template": {
|
||||
"full_content": "full_content.twig",
|
||||
"left_sidebar": "left_sidebar.twig",
|
||||
"right_sidebar": "right_sidebar.twig",
|
||||
"custom1": "custom1.twig",
|
||||
"guide": "guide.twig"
|
||||
}
|
||||
"title": "My Theme",
|
||||
"config": {
|
||||
"default_template": "full_content"
|
||||
},
|
||||
"template": {
|
||||
"full_content": "full_content.twig",
|
||||
"left_sidebar": "left_sidebar.twig",
|
||||
"right_sidebar": "right_sidebar.twig",
|
||||
"custom1": "custom1.twig",
|
||||
"guide": "guide.twig"
|
||||
},
|
||||
"header_color": "#0a369d"
|
||||
}
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
- `title` — Display name in admin
|
||||
- `config.default_template` — Default layout for pages without a `layout:` frontmatter (fallback for unknown layouts)
|
||||
- `template` — Mapping from layout name to `.twig` file
|
||||
|
||||
## Template mapping
|
||||
|
||||
The `template` mapping defines which layouts are available. Each key is a layout name, each value is the `.twig` file:
|
||||
|
||||
| Layout name | Twig file | Description |
|
||||
|-------------|-----------|-------------|
|
||||
| `full_content` | `full_content.twig` | Full width, no sidebar |
|
||||
| `left_sidebar` | `left_sidebar.twig` | Sidebar on the left |
|
||||
| `right_sidebar` | `right_sidebar.twig` | Sidebar on the right |
|
||||
| `custom1` | `custom1.twig` | Custom layout |
|
||||
| `guide` | `guide.twig` | Guide with sidebar (Navigation plugin) |
|
||||
|
||||
## Layout selection
|
||||
|
||||
- Layouts are chosen via the frontmatter `layout:` key in content files
|
||||
- Unknown layouts fall back to `config.default_template`
|
||||
- `ThemeManager::getTemplates()` returns the template mapping
|
||||
- `ThemeManager::getConfig()` returns the config section (including `default_template`)
|
||||
|
||||
## Guide layout
|
||||
|
||||
The `guide` layout is special for guides. `getGuidePage()` in `CodePressCMS.php` automatically injects the Navigation plugin into the metadata for sidebar navigation.
|
||||
- `title` - Display name in admin
|
||||
- `config.default_template` - Default layout for new pages
|
||||
- `template` - Mapping of layout names to .twig files
|
||||
- `header_color` - (optional) Admin sidebar color, defaults to `#0a369d`
|
||||
Reference in New Issue
Block a user