v2.6.1d (Lyra): Plugin i18n, plugin editor vernieuwd, media invoegen

Plugin internationalisatie: plugins hebben eigen language/ mappen. Systeem
plugins volgen admin taal (admin.php), content plugins volgen content taal
(site.php). Fallback chain: geselecteerd -> plugin default_language -> CMS
default. PluginManager/AdminPluginAPI/CMSAPI uitgebreid met
getPluginTranslations()/t(). plugin.json settings ondersteunen
label_key/help_key/option_label_key.

Plugin uniformiteit: alle 6 plugins hebben uniforme structuur (README.md,
assets/.gitkeep, language/nl|en/). plugins/README.md herschreven.
guide plugin-development.md (NL+EN) volledig herschreven.

Plugin editor vernieuwd: geneste bestandsbrowser zijbalk, nieuw bestand
aanmaken, uploaden naar assets/, verwijderen en verplaatsen. Nieuwe routes:
plugins-file-upload, plugins-file-delete, plugins-file-move. Path-traversal
bescherming + protected plugins geblokkeerd.

Media invoegen in editor: nieuw /admin/media-list JSON endpoint + herbruikbare
_media-modal.twig include. Plugin-context scant assets/ map. editor-toolbar.js
modeMap uitgebreid voor css/scss/js/json.

Plugin overzicht knoppen: alleen iconen met title/aria-label.

Tests: pentest 30/30, WCAG 2.1 AA 25/25.
This commit is contained in:
2026-08-18 13:49:52 +00:00
parent 10c72de859
commit 5edc929c13
64 changed files with 3503 additions and 366 deletions
+27 -19
View File
@@ -25,8 +25,10 @@ class Dashboard
'plugin' => 'Dashboard',
'route' => 'dashboard',
'label' => 'Dashboard',
'label_key' => 'menu_label',
'icon' => 'bi-speedometer2',
'section' => 'general',
'permission' => 'dashboard',
],
];
}
@@ -39,6 +41,12 @@ class Dashboard
$enabledPlugins = $this->api ? $this->api->getEnabledPlugins() : [];
$versionInfo = $this->api ? $this->api->getVersionInfo() : ['version' => '0.0.0'];
// Plugin translations (admin context). Falls back to plugin default_language.
$t = $this->api ? $this->api->getPluginTranslations('Dashboard') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
$stats = [
'pages' => $this->countFiles($contentDir, ['md', 'php', 'html']),
'directories' => $this->countDirs($contentDir),
@@ -68,20 +76,20 @@ class Dashboard
ob_start();
?>
<h2 class="mb-4"><i class="bi bi-speedometer2"></i> Dashboard</h2>
<h2 class="mb-4"><i class="bi bi-speedometer2"></i> <?= htmlspecialchars($tr('page_title')) ?></h2>
<div class="row g-4">
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header"><i class="bi bi-info-circle"></i> Site informatie</div>
<div class="card-header"><i class="bi bi-info-circle"></i> <?= htmlspecialchars($tr('site_info')) ?></div>
<div class="card-body">
<table class="table table-sm mb-0">
<tr><td class="text-muted">Site titel</td><td><?= htmlspecialchars($siteTitle) ?></td></tr>
<tr><td class="text-muted">Standaard taal</td><td><?= htmlspecialchars($defaultLang) ?></td></tr>
<tr><td class="text-muted">CodePress versie</td><td><?= htmlspecialchars($stats['cms_version']) ?></td></tr>
<tr><td class="text-muted">PHP versie</td><td><?= htmlspecialchars($stats['php_version']) ?></td></tr>
<tr><td class="text-muted">Besturingssysteem</td><td><?= htmlspecialchars($stats['os']) ?></td></tr>
<tr><td class="text-muted">Config geladen</td><td><?php if ($stats['config_exists']): ?><span class="badge bg-success">Ja</span><?php else: ?><span class="badge bg-danger">Nee</span><?php endif; ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('site_title')) ?></td><td><?= htmlspecialchars($siteTitle) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('default_lang')) ?></td><td><?= htmlspecialchars($defaultLang) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('cms_version')) ?></td><td><?= htmlspecialchars($stats['cms_version']) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('php_version')) ?></td><td><?= htmlspecialchars($stats['php_version']) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('os')) ?></td><td><?= htmlspecialchars($stats['os']) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('config_loaded')) ?></td><td><?php if ($stats['config_exists']): ?><span class="badge bg-success"><?= htmlspecialchars($tr('yes')) ?></span><?php else: ?><span class="badge bg-danger"><?= htmlspecialchars($tr('no')) ?></span><?php endif; ?></td></tr>
</table>
</div>
</div>
@@ -89,12 +97,12 @@ class Dashboard
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header"><i class="bi bi-folder2-open"></i> Content informatie</div>
<div class="card-header"><i class="bi bi-folder2-open"></i> <?= htmlspecialchars($tr('content_info')) ?></div>
<div class="card-body">
<table class="table table-sm mb-0">
<tr><td class="text-muted">Pagina's</td><td><span class="badge bg-primary"><?= $stats['pages'] ?></span></td></tr>
<tr><td class="text-muted">Mappen</td><td><span class="badge bg-warning text-dark"><?= $stats['directories'] ?></span></td></tr>
<tr><td class="text-muted">Content grootte</td><td><?= htmlspecialchars($stats['content_size']) ?></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('pages')) ?></td><td><span class="badge bg-primary"><?= $stats['pages'] ?></span></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('folders')) ?></td><td><span class="badge bg-warning text-dark"><?= $stats['directories'] ?></span></td></tr>
<tr><td class="text-muted"><?= htmlspecialchars($tr('content_size')) ?></td><td><?= htmlspecialchars($stats['content_size']) ?></td></tr>
</table>
</div>
</div>
@@ -103,8 +111,8 @@ class Dashboard
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="bi bi-plug"></i> Plugins</span>
<a href="/admin/plugins" class="btn btn-sm btn-outline-secondary">Beheren</a>
<span><i class="bi bi-plug"></i> <?= htmlspecialchars($tr('plugins')) ?></span>
<a href="/admin/plugins" class="btn btn-sm btn-outline-secondary"><?= htmlspecialchars($tr('manage')) ?></a>
</div>
<div class="card-body">
<table class="table table-sm mb-0">
@@ -113,22 +121,22 @@ class Dashboard
<td>
<i class="bi bi-plug-fill"></i> <?= htmlspecialchars($pluginName) ?>
<?php if ($info['type'] === 'system'): ?>
<span class="badge bg-secondary fs-7">systeem</span>
<span class="badge bg-secondary fs-7"><?= htmlspecialchars($tr('plugin_type_system')) ?></span>
<?php else: ?>
<span class="badge bg-info fs-7">content</span>
<span class="badge bg-info fs-7"><?= htmlspecialchars($tr('plugin_type_content')) ?></span>
<?php endif; ?>
</td>
<td>
<?php if ($info['enabled']): ?>
<span class="badge bg-success">Actief</span>
<span class="badge bg-success"><?= htmlspecialchars($tr('active')) ?></span>
<?php else: ?>
<span class="badge bg-secondary">Inactief</span>
<span class="badge bg-secondary"><?= htmlspecialchars($tr('inactive')) ?></span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($pluginOverview)): ?>
<tr><td colspan="2" class="text-muted text-center">Geen plugins gevonden.</td></tr>
<tr><td colspan="2" class="text-muted text-center"><?= htmlspecialchars($tr('no_plugins')) ?></td></tr>
<?php endif; ?>
</table>
</div>
+39
View File
@@ -0,0 +1,39 @@
# Dashboard Plugin
Toont site informatie, content statistieken en een plugin overzicht op het admin dashboard.
## Plugin type
**Systeem** plugin. De plugin-output volgt de geselecteerde **admin taal** (via `AdminPluginAPI::getPluginTranslations()` met `admin.php`).
## Bestandsstructuur
```
Dashboard/
├── Dashboard.php # Hoofd plugin class
├── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # Optionele CSS/JS (leeg)
└── language/ # Vertalingen
├── nl/admin.php # Nederlandse admin labels
└── en/admin.php # Engelse admin labels
```
## Functies
- **Site informatie**: Site titel, standaard taal, CodePress/PHP versie, OS, config status
- **Content informatie**: Aantal pagina's, mappen, content grootte
- **Plugin overzicht**: Lijst van alle plugins met type (systeem/content) en actief/inactief status
## Instellingen
Deze plugin heeft geen instelbare configuratie (`hasConfig: false`).
## Taal support
De plugin gebruikt `AdminPluginAPI::getPluginTranslations('Dashboard')` om labels op te halen. Fallback chain:
1. Geselecteerde admin taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
Beschikbare sleutels staan in `language/<lang>/admin.php`. Het admin-menu label wordt vertaald via `label_key: 'menu_label'` in `getAdminMenu()`.
View File
+34
View File
@@ -0,0 +1,34 @@
<?php
return [
// Menu
'menu_label' => 'Dashboard',
// Page header
'page_title' => 'Dashboard',
// Site information card
'site_info' => 'Site information',
'site_title' => 'Site title',
'default_lang' => 'Default language',
'cms_version' => 'CodePress version',
'php_version' => 'PHP version',
'os' => 'Operating system',
'config_loaded' => 'Config loaded',
'yes' => 'Yes',
'no' => 'No',
// Content information card
'content_info' => 'Content information',
'pages' => 'Pages',
'folders' => 'Folders',
'content_size' => 'Content size',
// Plugins card
'plugins' => 'Plugins',
'manage' => 'Manage',
'plugin_type_system' => 'system',
'plugin_type_content' => 'content',
'active' => 'Active',
'inactive' => 'Inactive',
'no_plugins' => 'No plugins found.',
];
+34
View File
@@ -0,0 +1,34 @@
<?php
return [
// Menu
'menu_label' => 'Dashboard',
// Page header
'page_title' => 'Dashboard',
// Site informatie card
'site_info' => 'Site informatie',
'site_title' => 'Site titel',
'default_lang' => 'Standaard taal',
'cms_version' => 'CodePress versie',
'php_version' => 'PHP versie',
'os' => 'Besturingssysteem',
'config_loaded' => 'Config geladen',
'yes' => 'Ja',
'no' => 'Nee',
// Content informatie card
'content_info' => 'Content informatie',
'pages' => "Pagina's",
'folders' => 'Mappen',
'content_size' => 'Content grootte',
// Plugins card
'plugins' => 'Plugins',
'manage' => 'Beheren',
'plugin_type_system' => 'systeem',
'plugin_type_content' => 'content',
'active' => 'Actief',
'inactive' => 'Inactief',
'no_plugins' => 'Geen plugins gevonden.',
];
+3 -1
View File
@@ -5,5 +5,7 @@
"description": "Toont site informatie, content statistieken en plugin overzicht op het dashboard.",
"type": "system",
"essential": true,
"hasConfig": false
"hasConfig": false,
"default_language": "nl",
"settings": []
}
+17 -6
View File
@@ -2,9 +2,9 @@
class GeoIPInfo
{
private ?CMSAPI $api = null;
private ?PluginAPIInterface $api = null;
public function setAPI(CMSAPI $api): void
public function setAPI(PluginAPIInterface $api): void
{
$this->api = $api;
}
@@ -23,7 +23,13 @@ class GeoIPInfo
public function getAdminMenu(): array
{
return [
['label' => 'GeoIP Info', 'route' => 'geoip-info', 'icon' => 'bi-globe2'],
[
'plugin' => 'GeoIPInfo',
'label' => 'GeoIP Info',
'label_key' => 'menu_label',
'route' => 'geoip-info',
'icon' => 'bi-globe2',
],
];
}
@@ -46,11 +52,16 @@ class GeoIPInfo
$country = GeoIP::getCountryName($countryCode);
$flag = GeoIP::getCountryFlagEmoji($countryCode);
echo '<h2 class="mb-4"><i class="bi bi-globe2"></i> GeoIP Info</h2>';
$t = $this->api ? $this->api->getPluginTranslations('GeoIPInfo') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
echo '<h2 class="mb-4"><i class="bi bi-globe2"></i> ' . htmlspecialchars($tr('page_title')) . '</h2>';
echo '<div class="card shadow-sm"><div class="card-body">';
echo '<table class="table table-sm">';
echo '<tr><td>IP adres</td><td><code>' . htmlspecialchars($ip) . '</code></td></tr>';
echo '<tr><td>Land</td><td>' . $flag . ' ' . htmlspecialchars($country) . '</td></tr>';
echo '<tr><td>' . htmlspecialchars($tr('ip_address')) . '</td><td><code>' . htmlspecialchars($ip) . '</code></td></tr>';
echo '<tr><td>' . htmlspecialchars($tr('country')) . '</td><td>' . $flag . ' ' . htmlspecialchars($country) . '</td></tr>';
echo '</table>';
echo '</div></div>';
}
+38
View File
@@ -0,0 +1,38 @@
# GeoIPInfo Plugin
Systeem plugin die GeoIP-informatie (IP adres en land) toont op een admin-pagina.
## Plugin type
**Systeem** plugin. De plugin-output volgt de geselecteerde **admin taal** (via `AdminPluginAPI::getPluginTranslations()` met `admin.php`).
## Bestandsstructuur
```
GeoIPInfo/
├── GeoIPInfo.php # Hoofd plugin class
├── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # Optionele CSS/JS (leeg)
└── language/ # Vertalingen
├── nl/admin.php # Nederlandse admin labels
└── en/admin.php # Engelse admin labels
```
## Functies
- Toont het IP adres van de bezoeker
- Toont het land (met vlag-emoji) gebaseerd op GeoIP lookup
## Instellingen
Deze plugin heeft geen instelbare configuratie (`hasConfig: false`).
## Taal support
De plugin gebruikt `AdminPluginAPI::getPluginTranslations('GeoIPInfo')` om labels op te halen. Fallback chain:
1. Geselecteerde admin taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
Beschikbare sleutels staan in `language/<lang>/admin.php`. Het admin-menu label wordt vertaald via `label_key: 'menu_label'` in `getAdminMenu()`.
View File
+10
View File
@@ -0,0 +1,10 @@
<?php
return [
// Menu
'menu_label' => 'GeoIP Info',
// Page
'page_title' => 'GeoIP Info',
'ip_address' => 'IP address',
'country' => 'Country',
];
+10
View File
@@ -0,0 +1,10 @@
<?php
return [
// Menu
'menu_label' => 'GeoIP Info',
// Page
'page_title' => 'GeoIP Info',
'ip_address' => 'IP adres',
'country' => 'Land',
];
+5 -1
View File
@@ -3,5 +3,9 @@
"version": "1.0.0",
"author": "CodePress",
"description": "Systeem plugin voor GeoIP informatie in admin",
"type": "system"
"type": "system",
"essential": false,
"hasConfig": false,
"default_language": "nl",
"settings": []
}
+14 -8
View File
@@ -20,16 +20,22 @@ class HTMLBlock
public function getSidebarContent(): string
{
$currentPage = $this->api ? $this->api->getCurrentPageTitle() : 'Onbekend';
// Content plugin: translations follow the current content language.
$t = $this->api ? $this->api->getPluginTranslations('HTMLBlock') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
$currentPage = $this->api ? $this->api->getCurrentPageTitle() : $tr('unknown');
$isHomepage = $this->api ? $this->api->isHomepage() : false;
$currentLang = $this->api ? $this->api->getCurrentLanguage() : 'nl';
$currentPath = $_GET['page'] ?? '';
$currentPath = preg_replace('/\.(md|php|html)$/', '', $currentPath);
$content = '
<p class="mb-2"><strong>Huidige pagina:</strong> ' . htmlspecialchars($currentPage) . '</p>
<p class="mb-2"><strong>Taal:</strong> ' . strtoupper($currentLang) . '</p>
<p class="mb-3"><strong>Homepage:</strong> ' . ($isHomepage ? 'Ja' : 'Nee') . '</p>';
<p class="mb-2"><strong>' . htmlspecialchars($tr('current_page')) . ':</strong> ' . htmlspecialchars($currentPage) . '</p>
<p class="mb-2"><strong>' . htmlspecialchars($tr('language')) . ':</strong> ' . strtoupper($currentLang) . '</p>
<p class="mb-3"><strong>' . htmlspecialchars($tr('homepage')) . ':</strong> ' . htmlspecialchars($isHomepage ? $tr('yes') : $tr('no')) . '</p>';
// Add page-specific content
if ($this->api) {
@@ -38,9 +44,9 @@ class HTMLBlock
$content .= '
<div class="alert alert-info mb-3">
<small>
<strong>Bestandsinfo:</strong><br>
Aangemaakt: ' . htmlspecialchars($fileInfo['created']) . '<br>
Gewijzigd: ' . htmlspecialchars($fileInfo['modified']) . '
<strong>' . htmlspecialchars($tr('file_info')) . ':</strong><br>
' . htmlspecialchars($tr('created')) . ': ' . htmlspecialchars($fileInfo['created']) . '<br>
' . htmlspecialchars($tr('modified')) . ': ' . htmlspecialchars($fileInfo['modified']) . '
</small>
</div>';
}
@@ -49,7 +55,7 @@ class HTMLBlock
$menu = $this->api->getMenu();
if (!empty($menu)) {
$content .= '
<h6>Quick Navigation</h6>
<h6>' . htmlspecialchars($tr('quick_navigation')) . '</h6>
<ul class="list-unstyled mb-3">';
foreach ($menu as $item) {
+30 -91
View File
@@ -1,100 +1,39 @@
# HTMLBlock Plugin
Deze plugin toont een custom HTML blok in de sidebar met pagina-informatie en navigatie.
Toont een custom HTML-blok in de sidebar van content-pagina's met pagina-informatie en quick navigation.
## Functies
## Plugin type
- **Pagina informatie**: Toont huidige pagina titel en metadata
- **Bestandsinfo**: Aanmaak- en wijzigingsdatums
- **Dynamische navigatie**: Genereert quick links uit het menu
- **Interactive controls**: Verversen en sidebar toggle
- **Responsive**: Werkt op desktop en mobiel
## Installatie
1. Kopieer de `HTMLBlock` map naar `plugins/`
2. De plugin wordt automatisch geladen
## Gebruik
De plugin wordt automatisch in de sidebar geladen en toont:
### Huidige Pagina Info
- Pagina titel
- Huidige taal
- Homepage status
### Bestandsinformatie
- Aanmaakdatum
- Laatste wijziging
- Bestandsgrootte
### Quick Navigation
- Dynamische links uit het CMS menu
- Automatische URL generatie
### Interactive Controls
- **Ververs Content**: Herlaadt de huidige pagina
- **Toggle Sidebar**: Toont/verbergt de sidebar
## Customization
De plugin content kan worden aangepast door de `getSidebarContent()` methode te wijzigen in `HTMLBlock.php`.
### Voorbeeld Custom Content
```php
public function getSidebarContent(): string
{
$currentPage = $this->api ? $this->api->getCurrentPageTitle() : 'Onbekend';
return '
<div class="card">
<div class="card-body">
<h5>Mijn Custom Block</h5>
<p>Huidige pagina: ' . htmlspecialchars($currentPage) . '</p>
</div>
</div>';
}
```
## API Integration
De plugin maakt gebruik van de CMS API voor:
- `getCurrentPageTitle()` - Huidige pagina titel
- `getCurrentLanguage()` - Huidige taal
- `isHomepage()` - Check of homepage
- `getCurrentPageFileInfo()` - Bestandsinformatie
- `getMenu()` - Menu structuur
- `createUrl($page, $lang)` - URL generatie
## Styling
De plugin gebruikt Bootstrap 5 classes:
- `card`, `card-header`, `card-body` voor kaarten
- `btn`, `btn-outline-primary` voor knoppen
- `list-unstyled` voor navigatie
## JavaScript
De plugin bevat JavaScript voor:
- Pagina verversen
- Sidebar toggle functionaliteit
- Dynamische content updates
## Development
De plugin is een goed voorbeeld voor:
- API integratie
- Dynamic content generatie
- User interface components
- Responsive design
**Content** plugin. De plugin-output volgt de geselecteerde **content taal** (via `CMSAPI::getPluginTranslations()` met `site.php`).
## Bestandsstructuur
```
HTMLBlock/
├── HTMLBlock.php # Hoofd plugin bestand
── README.md # Deze documentatie
```
├── HTMLBlock.php # Hoofd plugin class
── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # Optionele CSS/JS (leeg)
└── language/ # Vertalingen
├── nl/site.php # Nederlandse front-end labels
└── en/site.php # Engelse front-end labels
```
## Functies
- **Pagina informatie**: Toont huidige pagina titel en metadata
- **Bestandsinfo**: Aanmaak- en wijzigingsdatums
- **Quick Navigation**: Genereert quick links uit het CMS menu
## Instellingen
Deze plugin heeft geen instelbare configuratie (`hasConfig: false`).
## Taal support
De plugin gebruikt `CMSAPI::getPluginTranslations('HTMLBlock')` om labels op te halen. Fallback chain:
1. Geselecteerde content taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
Beschikbare sleutels staan in `language/<lang>/site.php`.
View File
+15
View File
@@ -0,0 +1,15 @@
<?php
return [
// Sidebar card
'title' => 'HTML Block',
'current_page' => 'Current page',
'language' => 'Language',
'homepage' => 'Homepage',
'yes' => 'Yes',
'no' => 'No',
'unknown' => 'Unknown',
'file_info' => 'File info',
'created' => 'Created',
'modified' => 'Modified',
'quick_navigation' => 'Quick Navigation',
];
+15
View File
@@ -0,0 +1,15 @@
<?php
return [
// Sidebar card
'title' => 'HTML Block',
'current_page' => 'Huidige pagina',
'language' => 'Taal',
'homepage' => 'Homepage',
'yes' => 'Ja',
'no' => 'Nee',
'unknown' => 'Onbekend',
'file_info' => 'Bestandsinfo',
'created' => 'Aangemaakt',
'modified' => 'Gewijzigd',
'quick_navigation' => 'Quick Navigation',
];
+3 -1
View File
@@ -5,5 +5,7 @@
"description": "Toont aangepaste HTML-blokken in de sidebar van content-pagina's.",
"type": "content",
"essential": false,
"hasConfig": false
"hasConfig": false,
"default_language": "nl",
"settings": []
}
+59 -9
View File
@@ -20,19 +20,63 @@ class Logs
public function getAdminMenu(): array
{
$config = $this->getPluginConfig();
$requiredRoles = $config['required_roles'] ?? ['bi-manager', 'site-admin', 'admin'];
return [
[
'plugin' => 'Logs',
'route' => 'logs',
'label' => 'Logs',
'label_key' => 'menu_label',
'icon' => 'bi-journal-text',
'section' => 'general',
'permission' => 'logs',
'required_roles' => $requiredRoles,
],
];
}
/**
* Get this plugin's runtime config (defaults + overrides).
*/
private function getPluginConfig(): array
{
$pluginDir = dirname(__DIR__);
$pluginJsonFile = $pluginDir . '/plugin.json';
$configJsonFile = $pluginDir . '/config.json';
$defaults = [];
if (file_exists($pluginJsonFile)) {
$pluginJson = json_decode(file_get_contents($pluginJsonFile), true) ?? [];
foreach ($pluginJson['settings'] ?? [] as $setting) {
if (isset($setting['key'])) {
$defaults[$setting['key']] = $setting['default'] ?? null;
}
}
}
$overrides = [];
if (file_exists($configJsonFile)) {
$overrides = json_decode(file_get_contents($configJsonFile), true) ?? [];
}
return array_merge($defaults, $overrides);
}
public function handleAdminRoute(string $action): ?string
{
$config = $this->getPluginConfig();
$maxLines = (int)($config['max_lines'] ?? 100);
$showAdminTab = $config['show_admin_tab'] ?? true;
$showRequestsTab = $config['show_requests_tab'] ?? true;
// System plugin: translations resolve against the admin language.
$t = $this->api ? $this->api->getPluginTranslations('Logs') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
$tab = $_GET['tab'] ?? 'admin';
$logDir = dirname(__DIR__, 2) . '/admin/storage/logs';
$logFile = $tab === 'requests' ? $logDir . '/requests.log' : $logDir . '/admin.log';
@@ -40,7 +84,7 @@ class Logs
$logs = [];
if (file_exists($logFile)) {
$lines = file($logFile) ?: [];
$lines = array_slice($lines, -100);
$lines = array_slice($lines, -$maxLines);
foreach ($lines as $line) {
if (preg_match('/^\[([^\]]+)\] \[([^\]]+)\] \[([^\]]+)\] (.+)$/', trim($line), $m)) {
$logs[] = [
@@ -56,31 +100,37 @@ class Logs
ob_start();
?>
<h2 class="mb-4"><i class="bi bi-journal-text"></i> Logs</h2>
<h2 class="mb-4"><i class="bi bi-journal-text"></i> <?= htmlspecialchars($tr('page_title')) ?></h2>
<?php if ($showAdminTab || $showRequestsTab): ?>
<ul class="nav nav-tabs mb-3">
<?php if ($showAdminTab): ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'admin' ? 'active' : '' ?>" href="/admin/logs?tab=admin">Admin</a>
<a class="nav-link <?= $tab === 'admin' ? 'active' : '' ?>" href="/admin/logs?tab=admin"><?= htmlspecialchars($tr('tab_admin')) ?></a>
</li>
<?php endif; ?>
<?php if ($showRequestsTab): ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'requests' ? 'active' : '' ?>" href="/admin/logs?tab=requests">Requests</a>
<a class="nav-link <?= $tab === 'requests' ? 'active' : '' ?>" href="/admin/logs?tab=requests"><?= htmlspecialchars($tr('tab_requests')) ?></a>
</li>
<?php endif; ?>
</ul>
<?php endif; ?>
<div class="card shadow-sm">
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Tijd</th>
<th>Level</th>
<th>IP</th>
<th>Bericht</th>
<th><?= htmlspecialchars($tr('col_time')) ?></th>
<th><?= htmlspecialchars($tr('col_level')) ?></th>
<th><?= htmlspecialchars($tr('col_ip')) ?></th>
<th><?= htmlspecialchars($tr('col_message')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($logs)): ?>
<tr><td colspan="4" class="text-muted text-center py-4">Geen logs gevonden.</td></tr>
<tr><td colspan="4" class="text-muted text-center py-4"><?= htmlspecialchars($tr('no_logs')) ?></td></tr>
<?php else: ?>
<?php foreach ($logs as $log): ?>
<tr>
+49
View File
@@ -0,0 +1,49 @@
# Logs Plugin
Toont admin activiteitlogs en front-end request logs met filter tabs op een admin-pagina.
## Plugin type
**Systeem** plugin. De plugin-output volgt de geselecteerde **admin taal** (via `AdminPluginAPI::getPluginTranslations()` met `admin.php`).
## Bestandsstructuur
```
Logs/
├── Logs.php # Hoofd plugin class
├── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # Optionele CSS/JS (leeg)
└── language/ # Vertalingen
├── nl/admin.php # Nederlandse admin labels
└── en/admin.php # Engelse admin labels
```
## Functies
- **Admin tab**: Toont admin activiteitlogs (uit `admin/storage/logs/admin.log`)
- **Requests tab**: Toont front-end request logs (uit `admin/storage/logs/requests.log`)
- Tabbladen kunnen via de instellingen in/uitgeschakeld worden
- Aantal getoonde regels is instelbaar (meest recente eerst)
## Instellingen
Deze plugin heeft instelbare configuratie (`hasConfig: true`). Instellingen worden gedefinieerd in `plugin.json` onder `settings` en overschreven via `config.json`.
| Sleutel | Type | Standaard | Beschrijving |
|---------|------|-----------|--------------|
| `required_roles` | multi-select | `bi-manager, site-admin, admin` | Rollen die deze plugin mogen zien |
| `max_lines` | number | `100` | Aantal logregels dat getoond wordt (meest recente eerst) |
| `show_admin_tab` | checkbox | `true` | Schakel de admin activiteit-log tab in of uit |
| `show_requests_tab` | checkbox | `true` | Schakel de front-end request-log tab in of uit |
De label- en help-teksten voor instellingen worden vertaald via `label_key`/`help_key` (verwijst naar sleutels in `language/<lang>/admin.php`).
## Taal support
De plugin gebruikt `AdminPluginAPI::getPluginTranslations('Logs')` om labels op te halen. Fallback chain:
1. Geselecteerde admin taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
Beschikbare sleutels staan in `language/<lang>/admin.php`. Het admin-menu label wordt vertaald via `label_key: 'menu_label'` in `getAdminMenu()`.
View File
+32
View File
@@ -0,0 +1,32 @@
<?php
return [
// Menu
'menu_label' => 'Logs',
// Page
'page_title' => 'Logs',
'tab_admin' => 'Admin',
'tab_requests' => 'Requests',
'col_time' => 'Time',
'col_level' => 'Level',
'col_ip' => 'IP',
'col_message' => 'Message',
'no_logs' => 'No logs found.',
'analytics_disabled' => 'Analytics is disabled.',
// Settings (plugin.json label_key/help_key)
'setting_required_roles' => 'Roles allowed to view this plugin',
'setting_required_roles_help' => 'Determines which roles can access the logs page in the admin panel. Admin always has access.',
'role_options' => [
'admin' => 'Admin',
'content-manager' => 'Content Manager',
'bi-manager' => 'BI Manager',
'site-admin' => 'Site Admin',
],
'setting_max_lines' => 'Maximum number of lines',
'setting_max_lines_help' => 'Number of log lines shown (most recent first).',
'setting_show_admin_tab' => 'Show admin log tab',
'setting_show_admin_tab_help' => 'Enable or disable the admin activity log tab.',
'setting_show_requests_tab' => 'Show request log tab',
'setting_show_requests_tab_help' => 'Enable or disable the front-end request log tab.',
];
+32
View File
@@ -0,0 +1,32 @@
<?php
return [
// Menu
'menu_label' => 'Logs',
// Page
'page_title' => 'Logs',
'tab_admin' => 'Admin',
'tab_requests' => 'Requests',
'col_time' => 'Tijd',
'col_level' => 'Level',
'col_ip' => 'IP',
'col_message' => 'Bericht',
'no_logs' => 'Geen logs gevonden.',
'analytics_disabled' => 'Analytics is uitgeschakeld.',
// Settings (plugin.json label_key/help_key)
'setting_required_roles' => 'Rollen die deze plugin mogen zien',
'setting_required_roles_help' => 'Bepaalt welke rollen toegang hebben tot de logs-pagina in het admin-paneel. Admin heeft altijd toegang.',
'role_options' => [
'admin' => 'Admin',
'content-manager' => 'Content Beheerder',
'bi-manager' => 'BI Beheerder',
'site-admin' => 'Site Admin',
],
'setting_max_lines' => 'Maximaal aantal regels',
'setting_max_lines_help' => 'Aantal logregels dat getoond wordt (meest recente eerst).',
'setting_show_admin_tab' => 'Toon admin-log tab',
'setting_show_admin_tab_help' => 'Schakel de admin activiteit-log tab in of uit.',
'setting_show_requests_tab' => 'Toon request-log tab',
'setting_show_requests_tab_help' => 'Schakel de front-end request-log tab in of uit.',
];
+39 -1
View File
@@ -5,5 +5,43 @@
"description": "Toont admin activiteitlogs en front-end request logs met filter tabs.",
"type": "system",
"essential": false,
"hasConfig": true
"hasConfig": true,
"default_language": "nl",
"settings": [
{
"key": "required_roles",
"label_key": "setting_required_roles",
"help_key": "setting_required_roles_help",
"option_label_key": "role_options",
"type": "multi-select",
"default": ["bi-manager", "site-admin", "admin"],
"options": {
"admin": "Admin",
"content-manager": "Content Beheerder",
"bi-manager": "BI Beheerder",
"site-admin": "Site Admin"
}
},
{
"key": "max_lines",
"label_key": "setting_max_lines",
"help_key": "setting_max_lines_help",
"type": "number",
"default": 100
},
{
"key": "show_admin_tab",
"label_key": "setting_show_admin_tab",
"help_key": "setting_show_admin_tab_help",
"type": "checkbox",
"default": true
},
{
"key": "show_requests_tab",
"label_key": "setting_show_requests_tab",
"help_key": "setting_show_requests_tab_help",
"type": "checkbox",
"default": true
}
]
}
+47
View File
@@ -0,0 +1,47 @@
# Navigation Plugin
Essentiële content plugin die navigatie toont voor handleidingen en content in de sidebar.
## Plugin type
**Content** plugin. De plugin-output volgt de geselecteerde **content taal** (via `CMSAPI::getPluginTranslations()` met `site.php`).
## Bestandsstructuur
```
Navigation/
├── Navigation.php # Hoofd plugin class
├── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # CSS/SCSS voor navigatie
│ ├── css/navigation.css
│ └── scss/navigation.scss
└── language/ # Vertalingen
├── nl/site.php # Nederlandse front-end labels
└── en/site.php # Engelse front-end labels
```
## Functies
- **Content navigatie**: Bouwt een sidebar navigatie uit de content-mapstructuur
- **Handleiding navigatie**: Bouwt een sidebar navigatie uit de guide-mapstructuur
- Detecteert automatisch of de huidige pagina een handleiding of content pagina is
- Toont mappen (met kinderen) en bestanden (.md, .php, .html)
- Strip taal-prefixen uit bestandsnamen (bijv. `nl.pagina.md``Pagina`)
## Instellingen
Deze plugin heeft geen instelbare configuratie (`hasConfig: false`). Wel levert hij een CSS-URL via `getCssUrl()`.
## Taal support
De plugin-output bestaat voornamelijk uit bestands- en mapnamen (die uit de content zelf komen), dus er zijn weinig te vertalen UI-strings. De `language/<lang>/site.php` bestanden bevatten momenteel alleen `plugin_title` voor uniformiteit met de andere plugins en toekomstige uitbreiding.
De plugin gebruikt `CMSAPI::getPluginTranslations('Navigation')` om labels op te halen. Fallback chain:
1. Geselecteerde content taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
## Beschermd
Deze plugin is **protected**: hardcoded in `public/admin.php` (`getProtectedPlugins()`) en kan niet worden uitgeschakeld, verwijderd of bewerkt via de admin interface.
View File
+7
View File
@@ -0,0 +1,7 @@
<?php
// Navigation is a content plugin. Its sidebar output is built from file and
// directory names, so there are very few translatable UI strings. This file
// exists for uniformity with the other plugins and to allow future labels.
return [
'plugin_title' => 'Navigation',
];
+7
View File
@@ -0,0 +1,7 @@
<?php
// Navigation is a content plugin. Its sidebar output is built from file and
// directory names, so there are very few translatable UI strings. This file
// exists for uniformity with the other plugins and to allow future labels.
return [
'plugin_title' => 'Navigatie',
];
+3 -1
View File
@@ -5,5 +5,7 @@
"description": "Essentiële navigatie plugin voor handleidingen en content",
"type": "content",
"essential": true,
"hasConfig": false
"hasConfig": false,
"default_language": "nl",
"settings": []
}
+76 -79
View File
@@ -1,102 +1,99 @@
# CodePress CMS Plugins
Deze map bevat plugins voor de CodePress CMS. Elke plugin heeft zijn eigen submap met de plugin code.
Deze map bevat alle plugins voor de CodePress CMS. Elke plugin heeft zijn eigen submap.
## Plugin Structuur
## Plugin overzicht
Elke plugin map moet het volgende bevatten:
| Plugin | Type | Essentieel | Beschrijving |
|--------|------|------------|--------------|
| Dashboard | system | ja | Site informatie, content statistieken en plugin overzicht op het dashboard |
| GeoIPInfo | system | nee | GeoIP-informatie (IP adres en land) in admin |
| HTMLBlock | content | nee | Custom HTML-blok in de sidebar van content-pagina's |
| Logs | system | nee | Admin activiteitlogs en front-end request logs met filter tabs |
| Navigation | content | ja | Navigatie voor handleidingen en content (beschermd) |
| Statistics | system | nee | Bezoekersstatistieken: views, unieke bezoekers, landen, top pagina's |
Elke plugin heeft een eigen `README.md` met specifieke documentatie.
## Plugin structuur (uniform)
Elke plugin-map heeft de volgende structuur:
```
PluginName/
├── PluginName.php # Hoofd plugin bestand
├── README.md # Plugin documentatie (optioneel)
├── config.json # Plugin configuratie (optioneel)
── assets/ # CSS, JS, images (optioneel)
├── css/
├── js/
└── images/
├── PluginName.php # Hoofd plugin class (naam = mapnaam)
├── plugin.json # Plugin metadata + instellingen-schema
├── README.md # Plugin documentatie
── config.json # Optionele runtime configuratie (overschrijft defaults)
├── assets/ # Optionele CSS/JS/SCSS (.gitkeep als leeg)
├── css/
└── scss/
└── language/ # Vertalingen
├── nl/
│ ├── admin.php # Admin labels (systeem plugins)
│ └── site.php # Front-end labels (content plugins)
└── en/
├── admin.php
└── site.php
```
## Beschikbare Plugins
## plugin.json velden
### HTMLBlock
Toont een custom HTML blok in de sidebar met pagina-informatie en navigatie.
| Veld | Waarde | Beschrijving |
|------|--------|--------------|
| `name` | string | Weergavenaam in admin |
| `version` | string | Versienummer |
| `author` | string | Auteur |
| `description` | string | Korte beschrijving |
| `type` | `"system"` of `"content"` | Systeem (blauwe badge) of content (groene badge) |
| `essential` | boolean | Essentiële plugins kunnen niet worden bewerkt/verwijderd |
| `hasConfig` | boolean | Toont een Config-knop in admin |
| `default_language` | string | Fallback taal voor plugin-vertalingen (bijv. `nl`) |
| `settings` | array | Instellingen-schema (zie hieronder) |
**Locatie:** `HTMLBlock/HTMLBlock.php`
### Instellingen-schema
**Functies:**
- Toont huidige pagina informatie
- Dynamische navigatie
- Bestandsinformatie
- Interactive controls
Elke instelling in `settings` ondersteunt:
## Plugin Development
| Veld | Beschrijving |
|------|--------------|
| `key` | Instelling-sleutel (opgeslagen in `config.json`) |
| `type` | `text`, `checkbox`, `number`, `select`, `multi-select` |
| `default` | Standaardwaarde |
| `label` | Hardcoded label (fallback als geen `label_key`) |
| `help` | Hardcoded help-tekst (fallback als geen `help_key`) |
| `label_key` | Sleutel in plugin `language/<lang>/admin.php` voor vertaald label |
| `help_key` | Sleutel in plugin `language/<lang>/admin.php` voor vertaalde help-tekst |
| `option_label_key` | Sleutel naar een array met optie-labels voor `select`/`multi-select` |
| `options` | Opties voor `select`/`multi-select` (`{waarde: label}`) |
### Basis Plugin Class
## Taal support
```php
<?php
- **Systeem plugins** volgen de geselecteerde **admin taal** (uit `config.admin_language`).
- **Content plugins** volgen de geselecteerde **content taal** (uit de URL of `config.language.default`).
class MyPlugin
{
private ?CMSAPI $api = null;
public function setAPI(CMSAPI $api): void
{
$this->api = $api;
}
public function getSidebarContent(): string
{
return '<div>Mijn plugin content</div>';
}
}
```
Fallback chain voor plugin-vertalingen:
1. Geselecteerde taal (`language/<lang>/admin.php` of `site.php`)
2. Plugin `default_language` (`plugin.json`)
3. CMS `language.default`
4. Lege array (sleutel wordt ongewijzigd getoond)
### Beschikbare API Methodes
Plugins halen hun vertalingen op via de API:
- Systeem plugins: `$this->api->getPluginTranslations('PluginName')` (AdminPluginAPI)
- Content plugins: `$this->api->getPluginTranslations('PluginName')` (CMSAPI)
- `getCurrentPage()` - Huidige pagina data
- `getCurrentPageTitle()` - Huidige pagina titel
- `getMenu()` - Menu structuur
- `getConfig($key)` - Configuratie waardes
- `translate($key)` - Vertalingen
- `getCurrentLanguage()` - Huidige taal
- `isHomepage()` - Check of homepage
- `getCurrentPageFileInfo()` - Bestandsinformatie
- `createUrl($page, $lang)` - URL generatie
## Uitgebreide documentatie
### Plugin Hooks
Zie de handleiding voor uitgebreide plugin development documentatie:
Plugins kunnen de volgende methodes implementeren:
- `getSidebarContent()` - Content voor sidebar
- `setAPI(CMSAPI $api)` - API injectie
## Configuratie
Plugins kunnen een `config.json` bestand hebben:
```json
{
"enabled": true,
"settings": {
"option1": "value1",
"option2": "value2"
}
}
```
- `guide/nl/codepress-developer/plugin-development.md`
- `guide/en/codepress-developer/plugin-development.md`
## Installatie
1. Maak een nieuwe map in `plugins/`
2. Plaats de plugin class in `PluginName/PluginName.php`
3. Optioneel: voeg README.md en config.json toe
4. De plugin wordt automatisch geladen door de CMS
## Best Practices
- Gebruik `htmlspecialchars()` voor output
- Implementeer `setAPI()` voor CMS toegang
- Volg PSR-12 coding standards
- Gebruik namespace indien nodig
- Documenteer je plugin met README.md
1. Maak een nieuwe map in `plugins/` (mapnaam = plugin class naam)
2. Voeg de hoofd plugin class toe als `<Naam>.php`
3. Maak een `plugin.json` met metadata en (optioneel) instellingen-schema
4. Voeg een `README.md` toe met plugin documentatie
5. Voeg `language/<lang>/admin.php` of `site.php` toe voor vertalingen
6. Schakel de plugin in via `enabled_plugins` in `config.json` (of via de admin Plugins-pagina)
+51
View File
@@ -0,0 +1,51 @@
# Statistics Plugin
Toont bezoekersstatistieken: totaal aantal views, unieke bezoekers, landen en top pagina's op een admin-pagina.
## Plugin type
**Systeem** plugin. De plugin-output volgt de geselecteerde **admin taal** (via `AdminPluginAPI::getPluginTranslations()` met `admin.php`).
## Bestandsstructuur
```
Statistics/
├── Statistics.php # Hoofd plugin class
├── plugin.json # Plugin metadata + instellingen
├── README.md # Dit bestand
├── assets/ # Optionele CSS/JS (leeg)
└── language/ # Vertalingen
├── nl/admin.php # Nederlandse admin labels
└── en/admin.php # Engelse admin labels
```
## Functies
- **Totaal aantal views**: Totaal aantal paginaweergaven
- **Unieke bezoekers**: Aantal unieke bezoekers
- **Pagina views**: Totaal aantal pagina views
- **Landen**: Overzicht van bezoekers per land (met vlag-emoji)
- **Top pagina's**: Meest bezochte pagina's
Statistieken worden alleen getoond als Analytics is ingeschakeld in de site configuratie; anders wordt een waarschuwing getoond.
## Instellingen
Deze plugin heeft instelbare configuratie (`hasConfig: true`). Instellingen worden gedefinieerd in `plugin.json` onder `settings` en overschreven via `config.json`.
| Sleutel | Type | Standaard | Beschrijving |
|---------|------|-----------|--------------|
| `required_roles` | multi-select | `bi-manager, site-admin, admin` | Rollen die deze plugin mogen zien |
| `show_countries` | checkbox | `true` | Schakel het landen-overzicht in of uit |
| `show_top_pages` | checkbox | `true` | Schakel het top pagina's overzicht in of uit |
De label- en help-teksten voor instellingen worden vertaald via `label_key`/`help_key` (verwijst naar sleutels in `language/<lang>/admin.php`).
## Taal support
De plugin gebruikt `AdminPluginAPI::getPluginTranslations('Statistics')` om labels op te halen. Fallback chain:
1. Geselecteerde admin taal
2. Plugin `default_language` (`nl`)
3. Lege array (sleutel wordt ongewijzigd getoond)
Beschikbare sleutels staan in `language/<lang>/admin.php`. Het admin-menu label wordt vertaald via `label_key: 'menu_label'` in `getAdminMenu()`.
+60 -11
View File
@@ -20,24 +20,67 @@ class Statistics
public function getAdminMenu(): array
{
$config = $this->getPluginConfig();
$requiredRoles = $config['required_roles'] ?? ['bi-manager', 'site-admin', 'admin'];
return [
[
'plugin' => 'Statistics',
'route' => 'statistics',
'label' => 'Statistieken',
'label_key' => 'menu_label',
'icon' => 'bi-bar-chart',
'section' => 'general',
'permission' => 'statistics',
'required_roles' => $requiredRoles,
],
];
}
/**
* Get this plugin's runtime config (defaults + overrides).
*/
private function getPluginConfig(): array
{
$pluginDir = dirname(__DIR__);
$pluginJsonFile = $pluginDir . '/plugin.json';
$configJsonFile = $pluginDir . '/config.json';
$defaults = [];
if (file_exists($pluginJsonFile)) {
$pluginJson = json_decode(file_get_contents($pluginJsonFile), true) ?? [];
foreach ($pluginJson['settings'] ?? [] as $setting) {
if (isset($setting['key'])) {
$defaults[$setting['key']] = $setting['default'] ?? null;
}
}
}
$overrides = [];
if (file_exists($configJsonFile)) {
$overrides = json_decode(file_get_contents($configJsonFile), true) ?? [];
}
return array_merge($defaults, $overrides);
}
public function handleAdminRoute(string $action): ?string
{
// System plugin: translations resolve against the admin language.
$t = $this->api ? $this->api->getPluginTranslations('Statistics') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
$analytics = $this->getAnalytics();
if ($analytics === null) {
return '<div class="alert alert-warning">Analytics is uitgeschakeld.</div>';
return '<div class="alert alert-warning">' . htmlspecialchars($tr('analytics_disabled')) . '</div>';
}
$config = $this->getPluginConfig();
$showCountries = $config['show_countries'] ?? true;
$showTopPages = $config['show_top_pages'] ?? true;
$stats = $analytics->getStats();
$totals = $stats['totals'] ?? ['views' => 0, 'uniques' => 0, 'pages' => []];
$countries = $stats['countries'] ?? [];
@@ -45,14 +88,14 @@ class Statistics
ob_start();
?>
<h2 class="mb-4"><i class="bi bi-bar-chart"></i> Statistieken</h2>
<h2 class="mb-4"><i class="bi bi-bar-chart"></i> <?= htmlspecialchars($tr('page_title')) ?></h2>
<div class="row g-4 mb-4">
<div class="col-md-4">
<div class="card stat-card shadow-sm">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1">Totaal aantal views</h6>
<h6 class="text-muted mb-1"><?= htmlspecialchars($tr('total_views')) ?></h6>
<h3 class="mb-0"><?= number_format($totals['views'] ?? 0, 0, ',', '.') ?></h3>
</div>
<i class="bi bi-eye stat-icon text-primary"></i>
@@ -63,7 +106,7 @@ class Statistics
<div class="card stat-card shadow-sm">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1">Unieke bezoekers</h6>
<h6 class="text-muted mb-1"><?= htmlspecialchars($tr('unique_visitors')) ?></h6>
<h3 class="mb-0"><?= number_format($totals['uniques'] ?? 0, 0, ',', '.') ?></h3>
</div>
<i class="bi bi-people stat-icon text-success"></i>
@@ -74,7 +117,7 @@ class Statistics
<div class="card stat-card shadow-sm">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-1">Pagina views</h6>
<h6 class="text-muted mb-1"><?= htmlspecialchars($tr('page_views')) ?></h6>
<h3 class="mb-0"><?= number_format($totals['page_views'] ?? ($totals['views'] ?? 0), 0, ',', '.') ?></h3>
</div>
<i class="bi bi-file-earmark-text stat-icon text-info"></i>
@@ -83,16 +126,18 @@ class Statistics
</div>
</div>
<?php if ($showCountries || $showTopPages): ?>
<div class="row g-4">
<?php if ($showCountries): ?>
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header"><i class="bi bi-globe"></i> Landen</div>
<div class="card-header"><i class="bi bi-globe"></i> <?= htmlspecialchars($tr('countries')) ?></div>
<div class="card-body">
<?php if (empty($countries)): ?>
<p class="text-muted mb-0">Geen data</p>
<p class="text-muted mb-0"><?= htmlspecialchars($tr('no_data')) ?></p>
<?php else: ?>
<table class="table table-sm mb-0">
<thead><tr><th>Land</th><th>Views</th></tr></thead>
<thead><tr><th><?= htmlspecialchars($tr('col_country')) ?></th><th><?= htmlspecialchars($tr('col_views')) ?></th></tr></thead>
<tbody>
<?php foreach ($countries as $country => $count): ?>
<tr>
@@ -106,15 +151,17 @@ class Statistics
</div>
</div>
</div>
<?php endif; ?>
<?php if ($showTopPages): ?>
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header"><i class="bi bi-file-earmark-bar"></i> Top pagina's</div>
<div class="card-header"><i class="bi bi-file-earmark-bar"></i> <?= htmlspecialchars($tr('top_pages')) ?></div>
<div class="card-body">
<?php if (empty($topPages)): ?>
<p class="text-muted mb-0">Geen data</p>
<p class="text-muted mb-0"><?= htmlspecialchars($tr('no_data')) ?></p>
<?php else: ?>
<table class="table table-sm mb-0">
<thead><tr><th>Pagina</th><th>Views</th></tr></thead>
<thead><tr><th><?= htmlspecialchars($tr('col_page')) ?></th><th><?= htmlspecialchars($tr('col_views')) ?></th></tr></thead>
<tbody>
<?php foreach ($topPages as $page => $count): ?>
<tr>
@@ -128,7 +175,9 @@ class Statistics
</div>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php
return ob_get_clean();
}
View File
+32
View File
@@ -0,0 +1,32 @@
<?php
return [
// Menu
'menu_label' => 'Statistics',
// Page
'page_title' => 'Statistics',
'total_views' => 'Total views',
'unique_visitors' => 'Unique visitors',
'page_views' => 'Page views',
'countries' => 'Countries',
'top_pages' => 'Top pages',
'no_data' => 'No data',
'col_country' => 'Country',
'col_views' => 'Views',
'col_page' => 'Page',
'analytics_disabled' => 'Analytics is disabled.',
// Settings (plugin.json label_key/help_key)
'setting_required_roles' => 'Roles allowed to view this plugin',
'setting_required_roles_help' => 'Determines which roles can access the statistics page in the admin panel. Admin always has access.',
'role_options' => [
'admin' => 'Admin',
'content-manager' => 'Content Manager',
'bi-manager' => 'BI Manager',
'site-admin' => 'Site Admin',
],
'setting_show_countries' => 'Show countries overview',
'setting_show_countries_help' => 'Enable or disable the countries overview on the statistics page.',
'setting_show_top_pages' => 'Show top pages',
'setting_show_top_pages_help' => 'Enable or disable the top pages overview.',
];
+32
View File
@@ -0,0 +1,32 @@
<?php
return [
// Menu
'menu_label' => 'Statistieken',
// Page
'page_title' => 'Statistieken',
'total_views' => 'Totaal aantal views',
'unique_visitors' => 'Unieke bezoekers',
'page_views' => 'Pagina views',
'countries' => 'Landen',
'top_pages' => "Top pagina's",
'no_data' => 'Geen data',
'col_country' => 'Land',
'col_views' => 'Views',
'col_page' => 'Pagina',
'analytics_disabled' => 'Analytics is uitgeschakeld.',
// Settings (plugin.json label_key/help_key)
'setting_required_roles' => 'Rollen die deze plugin mogen zien',
'setting_required_roles_help' => 'Bepaalt welke rollen toegang hebben tot de statistieken-pagina in het admin-paneel. Admin heeft altijd toegang.',
'role_options' => [
'admin' => 'Admin',
'content-manager' => 'Content Beheerder',
'bi-manager' => 'BI Beheerder',
'site-admin' => 'Site Admin',
],
'setting_show_countries' => 'Toon landen-overzicht',
'setting_show_countries_help' => 'Schakel het landen-overzicht in of uit op de statistieken-pagina.',
'setting_show_top_pages' => "Toon top pagina's",
'setting_show_top_pages_help' => "Schakel het top pagina's overzicht in of uit.",
];
+32 -1
View File
@@ -5,5 +5,36 @@
"description": "Bezoekersstatistieken: totaal aantal views, unieke bezoekers, landen en top pagina's.",
"type": "system",
"essential": false,
"hasConfig": true
"hasConfig": true,
"default_language": "nl",
"settings": [
{
"key": "required_roles",
"label_key": "setting_required_roles",
"help_key": "setting_required_roles_help",
"option_label_key": "role_options",
"type": "multi-select",
"default": ["bi-manager", "site-admin", "admin"],
"options": {
"admin": "Admin",
"content-manager": "Content Beheerder",
"bi-manager": "BI Beheerder",
"site-admin": "Site Admin"
}
},
{
"key": "show_countries",
"label_key": "setting_show_countries",
"help_key": "setting_show_countries_help",
"type": "checkbox",
"default": true
},
{
"key": "show_top_pages",
"label_key": "setting_show_top_pages",
"help_key": "setting_show_top_pages_help",
"type": "checkbox",
"default": true
}
]
}