Vervang raw JSON config editor door instellingenformulier met default_page dropdown

- Admin config pagina is nu een formulier met secties voor algemeen, taal,
  SEO, auteur en features
- Startpagina instelbaar via dropdown met beschikbare pagina's uit content/
- Handleiding (nl/en) bijgewerkt met nieuwe configuratie-instructies
This commit is contained in:
2026-07-28 13:49:33 +02:00
parent 4bb138eb92
commit e85f6e91e1
4 changed files with 218 additions and 55 deletions
+134 -14
View File
@@ -1,19 +1,139 @@
<h2 class="mb-4"><i class="bi bi-sliders"></i> Configuratie</h2> <h2 class="mb-4"><i class="bi bi-sliders"></i> Configuratie</h2>
<div class="card shadow-sm"> <?php if ($message): ?>
<div class="card-header"> <div class="alert alert-<?= $messageType ?> alert-dismissible fade show">
<i class="bi bi-filetype-json"></i> config.json <?= htmlspecialchars($message) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div> </div>
<div class="card-body"> <?php endif; ?>
<form method="POST" action="/admin/config">
<input type="hidden" name="csrf_token" value="<?= $csrf ?>"> <form method="POST" action="/admin/config">
<div class="mb-3"> <input type="hidden" name="csrf_token" value="<?= $csrf ?>">
<textarea name="config_content" class="form-control font-monospace" rows="25" style="font-size: 0.9rem; tab-size: 4;"><?= htmlspecialchars($siteConfig) ?></textarea>
<small class="form-text text-muted">Bewerk de JSON configuratie. Ongeldige JSON wordt niet opgeslagen.</small> <div class="card shadow-sm mb-4">
<div class="card-header">
<i class="bi bi-globe"></i> Algemene instellingen
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-6">
<label for="site_title" class="form-label">Site titel</label>
<input type="text" class="form-control" id="site_title" name="site_title"
value="<?= htmlspecialchars($configData['site_title'] ?? 'CodePress') ?>">
</div>
<div class="col-md-6">
<label for="default_page" class="form-label">Standaard/startpagina</label>
<select name="default_page" id="default_page" class="form-select">
<option value="">-- Selecteer --</option>
<?php foreach ($availablePages as $page): ?>
<option value="<?= htmlspecialchars($page) ?>" <?= ($configData['default_page'] ?? '') === $page ? 'selected' : '' ?>>
<?= htmlspecialchars(ucwords(str_replace(['-', '_'], ' ', $page))) ?>
</option>
<?php endforeach; ?>
</select>
<small class="form-text text-muted">Welke pagina wordt getoond als bezoekers de site openen.</small>
</div>
</div> </div>
<button type="submit" class="btn btn-primary"> </div>
<i class="bi bi-check-lg"></i> Opslaan
</button>
</form>
</div> </div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header">
<i class="bi bi-translate"></i> Taal
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-4">
<label for="language_default" class="form-label">Standaard taal</label>
<select name="language_default" id="language_default" class="form-select">
<?php foreach (['nl' => 'Nederlands', 'en' => 'English'] as $code => $label): ?>
<option value="<?= $code ?>" <?= ($configData['language']['default'] ?? 'nl') === $code ? 'selected' : '' ?>>
<?= $label ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-8">
<label class="form-label">Beschikbare talen</label>
<div>
<?php $availLangs = $configData['language']['available'] ?? ['nl', 'en']; ?>
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="lang_nl" name="language_available[]" value="nl" <?= in_array('nl', $availLangs) ? 'checked' : '' ?>>
<label class="form-check-label" for="lang_nl">Nederlands</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="lang_en" name="language_available[]" value="en" <?= in_array('en', $availLangs) ? 'checked' : '' ?>>
<label class="form-check-label" for="lang_en">English</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header">
<i class="bi bi-search"></i> SEO
</div>
<div class="card-body">
<div class="mb-3">
<label for="seo_description" class="form-label">Meta beschrijving</label>
<textarea class="form-control" id="seo_description" name="seo_description" rows="2"><?= htmlspecialchars($configData['seo']['description'] ?? '') ?></textarea>
</div>
<div class="mb-3">
<label for="seo_keywords" class="form-label">Meta keywords</label>
<input type="text" class="form-control" id="seo_keywords" name="seo_keywords"
value="<?= htmlspecialchars($configData['seo']['keywords'] ?? '') ?>">
<small class="form-text text-muted">Komma-gescheiden trefwoorden.</small>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header">
<i class="bi bi-person"></i> Auteur
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-6">
<label for="author_name" class="form-label">Naam</label>
<input type="text" class="form-control" id="author_name" name="author_name"
value="<?= htmlspecialchars($configData['author']['name'] ?? '') ?>">
</div>
<div class="col-md-6">
<label for="author_website" class="form-label">Website</label>
<input type="url" class="form-control" id="author_website" name="author_website"
value="<?= htmlspecialchars($configData['author']['website'] ?? '') ?>">
</div>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-header">
<i class="bi bi-toggle-on"></i> Features
</div>
<div class="card-body">
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input" id="feature_auto_link" name="feature_auto_link" value="1" <?= !empty($configData['features']['auto_link_pages']) ? 'checked' : '' ?>>
<label class="form-check-label" for="feature_auto_link">Automatisch pagina's linken</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input" id="feature_search" name="feature_search" value="1" <?= !empty($configData['features']['search_enabled']) ? 'checked' : '' ?>>
<label class="form-check-label" for="feature_search">Zoekfunctie inschakelen</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input" id="feature_breadcrumbs" name="feature_breadcrumbs" value="1" <?= !empty($configData['features']['breadcrumbs_enabled']) ? 'checked' : '' ?>>
<label class="form-check-label" for="feature_breadcrumbs">Breadcrumbs tonen</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="show_version" name="show_version" value="1" <?= !empty($configData['show_version']) ? 'checked' : '' ?>>
<label class="form-check-label" for="show_version">CMS versie tonen in footer</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-lg">
<i class="bi bi-check-lg"></i> Configuratie opslaan
</button>
</form>
+20 -12
View File
@@ -31,8 +31,8 @@ CodePress CMS is a lightweight, file-based content management system built with
- Search URL: `?search=query` - Search URL: `?search=query`
### Configuration ### Configuration
- **JSON configuration** in `config.json` - **Settings form** in admin panel via `/admin/config`
- Dynamic homepage setting (`default_page`) - Dynamic homepage setting via dropdown
- SEO settings (description, keywords) - SEO settings (description, keywords)
- Author information with links - Author information with links
- Theme configuration via `themes/` directory - Theme configuration via `themes/` directory
@@ -58,7 +58,7 @@ CodePress CMS is a lightweight, file-based content management system built with
- **CodeMirror editor** with toolbar (bold, italic, heading, link, image, list, media insert) - **CodeMirror editor** with toolbar (bold, italic, heading, link, image, list, media insert)
- **Media browser modal** in the editor for selecting images/files - **Media browser modal** in the editor for selecting images/files
- **Directory management** - Create, rename and delete directories (empty only) - **Directory management** - Create, rename and delete directories (empty only)
- **Configuration editor** - Edit `config.json` with JSON validation - **Configuration editor** - Settings form for site configuration (title, homepage, language, SEO, author, features)
- **Theme management** - Create, activate, edit (colors, background) and delete themes - **Theme management** - Create, activate, edit (colors, background) and delete themes
- **Plugin management** - Overview, create, edit, configure, enable/disable and delete - **Plugin management** - Overview, create, edit, configure, enable/disable and delete
- **Media management** - Upload and delete media files in `content/-assets/` - **Media management** - Upload and delete media files in `content/-assets/`
@@ -157,7 +157,19 @@ codepress/
## Configuration ## Configuration
### Basic Configuration (`config.json`) ### Basic Configuration
The site configuration is managed via the **admin panel** at `/admin/config`. Here you'll find a settings form with the following sections:
- **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
The configuration is stored in `config.json`. You can also edit this file manually for advanced options like `active_theme`, `content_dir` and `templates_dir`.
### Example `config.json`
```json ```json
{ {
@@ -186,11 +198,6 @@ codepress/
} }
``` ```
- `active_theme` - Determines which theme from `themes/` is active
- `templates_dir` - Directory with Mustache templates (`cms/templates`)
- `default_page` - Default page (e.g. `index`)
- `content_dir` - Directory with content files (usually not changed)
### Theme Configuration (`themes/<name>/theme.json`) ### Theme Configuration (`themes/<name>/theme.json`)
```json ```json
@@ -268,7 +275,7 @@ Media files (images, PDFs, video, audio) can be placed in any `content/` subdire
| `content-dir-rename` | Rename directory | | `content-dir-rename` | Rename directory |
| `content-dir-create` | Create directory (POST) | | `content-dir-create` | Create directory (POST) |
| `content-dir-delete` | Delete directory (empty only) | | `content-dir-delete` | Delete directory (empty only) |
| `config` | Configuration editor (`config.json`) | | `config` | Configuration form (title, homepage, language, SEO, author, features) |
| `theme` | Theme management | | `theme` | Theme management |
| `plugins` | Plugin overview | | `plugins` | Plugin overview |
| `plugins-new` | Create new plugin | | `plugins-new` | Create new plugin |
@@ -499,8 +506,9 @@ The MQTTTracker plugin stores `broker_host`, `broker_port`, `client_id`, `userna
### How do I set the homepage? ### How do I set the homepage?
1. **Automatic**: Set `"default_page": "index"` in `config.json` (or omit the field) 1. Go to **Configuration** in the admin panel (`/admin/config`)
2. **Manual**: Set `"default_page": "page-name"` in `config.json` 2. Select the desired page in the **Default/homepage** dropdown
3. Click **Save configuration**
### How does navigation work? ### How does navigation work?
+20 -12
View File
@@ -31,8 +31,8 @@ CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd
- Search URL: `?search=zoekterm` - Search URL: `?search=zoekterm`
### Configuratie ### Configuratie
- **JSON configuratie** in `config.json` - **Instellingenformulier** in admin paneel via `/admin/config`
- Dynamische homepage instelling (`default_page`) - Dynamische homepage instelling via dropdown
- SEO instellingen (description, keywords) - SEO instellingen (description, keywords)
- Author informatie met links - Author informatie met links
- Thema configuratie via `themes/` directory - Thema configuratie via `themes/` directory
@@ -58,7 +58,7 @@ CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd
- **CodeMirror editor** met toolbar (vet, cursief, kop, link, afbeelding, lijst, media invoegen) - **CodeMirror editor** met toolbar (vet, cursief, kop, link, afbeelding, lijst, media invoegen)
- **Media browser modal** in de editor voor het selecteren van afbeeldingen/bestanden - **Media browser modal** in de editor voor het selecteren van afbeeldingen/bestanden
- **Map beheer** - Mappen aanmaken, hernoemen en verwijderen (alleen lege mappen) - **Map beheer** - Mappen aanmaken, hernoemen en verwijderen (alleen lege mappen)
- **Configuratie editor** - `config.json` bewerken met JSON-validatie - **Configuratie editor** - Instellingenformulier voor site configuratie (titel, startpagina, taal, SEO, auteur, features)
- **Thema beheer** - Thema's aanmaken, activeren, bewerken (kleuren, achtergrond) en verwijderen - **Thema beheer** - Thema's aanmaken, activeren, bewerken (kleuren, achtergrond) en verwijderen
- **Plugin beheer** - Overzicht, aanmaken, bewerken, configureren, in-/uitschakelen en verwijderen - **Plugin beheer** - Overzicht, aanmaken, bewerken, configureren, in-/uitschakelen en verwijderen
- **Media beheer** - Uploaden en verwijderen van mediabestanden in `content/-assets/` - **Media beheer** - Uploaden en verwijderen van mediabestanden in `content/-assets/`
@@ -157,7 +157,19 @@ codepress/
## Configuratie ## Configuratie
### Basis Configuratie (`config.json`) ### Basis Configuratie
De site configuratie wordt beheerd via het **admin paneel** op `/admin/config`. Hier vind je een instellingenformulier met de volgende secties:
- **Algemene instellingen** - Site titel en startpagina (dropdown met beschikbare pagina's)
- **Taal** - Standaard taal en beschikbare talen
- **SEO** - Meta beschrijving en keywords
- **Auteur** - Naam en website
- **Features** - Auto-link pagina's, zoekfunctie, breadcrumbs, versie tonen
De configuratie wordt opgeslagen in `config.json`. Je kunt dit bestand ook handmatig bewerken voor geavanceerde opties zoals `active_theme`, `content_dir` en `templates_dir`.
### Voorbeeld `config.json`
```json ```json
{ {
@@ -186,11 +198,6 @@ codepress/
} }
``` ```
- `active_theme` - Bepaalt welk thema uit `themes/` actief is
- `templates_dir` - Map met Mustache templates (`cms/templates`)
- `default_page` - Standaard pagina (bijv. `index`)
- `content_dir` - Map met content bestanden (wordt gewoonlijk niet gewijzigd)
### Thema Configuratie (`themes/<naam>/theme.json`) ### Thema Configuratie (`themes/<naam>/theme.json`)
```json ```json
@@ -269,7 +276,7 @@ Media bestanden (afbeeldingen, PDFs, video, audio) kunnen in elke `content/` sub
| `content-dir-rename` | Map hernoemen | | `content-dir-rename` | Map hernoemen |
| `content-dir-create` | Map aanmaken (POST) | | `content-dir-create` | Map aanmaken (POST) |
| `content-dir-delete` | Map verwijderen (alleen leeg) | | `content-dir-delete` | Map verwijderen (alleen leeg) |
| `config` | Configuratie editor (`config.json`) | | `config` | Configuratieformulier (titel, startpagina, taal, SEO, auteur, features) |
| `theme` | Thema beheer | | `theme` | Thema beheer |
| `plugins` | Plugin overzicht | | `plugins` | Plugin overzicht |
| `plugins-new` | Nieuwe plugin aanmaken | | `plugins-new` | Nieuwe plugin aanmaken |
@@ -500,8 +507,9 @@ De MQTTTracker plugin slaat `broker_host`, `broker_port`, `client_id`, `username
### Hoe stel ik de homepage in? ### Hoe stel ik de homepage in?
1. **Automatisch**: Zet `"default_page": "index"` in `config.json` (of verwijder het veld) 1. Ga naar **Configuratie** in het admin paneel (`/admin/config`)
2. **Handmatig**: Stel `"default_page": "pagina-naam"` in `config.json` 2. Selecteer de gewenste pagina in het **Standaard/startpagina** dropdown
3. Klik op **Configuratie opslaan
### Hoe werkt de navigatie? ### Hoe werkt de navigatie?
+44 -17
View File
@@ -658,29 +658,56 @@ function handleConfig(AdminAuth $auth, array $config): void
$message = ''; $message = '';
$messageType = ''; $messageType = '';
// Load current config
$configData = file_exists($configJson) ? json_decode(file_get_contents($configJson), true) : [];
if (!is_array($configData)) $configData = [];
// Get available root-level pages
$contentDir = $config['content_dir'];
$availablePages = [];
$availableLangs = $configData['language']['available'] ?? ['nl', 'en'];
if (is_dir($contentDir)) {
$pageMap = [];
foreach (scandir($contentDir) as $file) {
if ($file[0] === '.') continue;
$filePath = $contentDir . '/' . $file;
if (is_file($filePath) && preg_match('/\.(md|php|html)$/', $file)) {
$base = $file;
$langPattern = '/^(' . implode('|', $availableLangs) . ')\.(.+)$/';
if (preg_match($langPattern, $base, $m)) {
$base = $m[2];
}
$pageKey = preg_replace('/\.(md|php|html)$/', '', $base);
$pageMap[$pageKey] = true;
}
}
$availablePages = array_keys($pageMap);
sort($availablePages);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) { if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
$message = 'Ongeldige CSRF token.'; $message = 'Ongeldige CSRF token.';
$messageType = 'danger'; $messageType = 'danger';
} else { } else {
$jsonContent = $_POST['config_content'] ?? ''; $configData['site_title'] = trim($_POST['site_title'] ?? 'CodePress');
$parsed = json_decode($jsonContent, true); $configData['default_page'] = trim($_POST['default_page'] ?? 'index');
if ($parsed === null && json_last_error() !== JSON_ERROR_NONE) { $configData['language']['default'] = trim($_POST['language_default'] ?? 'nl');
$message = 'Ongeldige JSON: ' . json_last_error_msg(); $configData['language']['available'] = $_POST['language_available'] ?? ['nl', 'en'];
$messageType = 'danger'; $configData['seo']['description'] = trim($_POST['seo_description'] ?? '');
} else { $configData['seo']['keywords'] = trim($_POST['seo_keywords'] ?? '');
file_put_contents($configJson, json_encode($parsed, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); $configData['author']['name'] = trim($_POST['author_name'] ?? '');
adminLog($config, 'info', $user['username'] . ' wijzigde site configuratie'); $configData['author']['website'] = trim($_POST['author_website'] ?? '');
$message = 'Configuratie opgeslagen.'; $configData['show_version'] = !empty($_POST['show_version']);
$messageType = 'success'; $configData['features']['auto_link_pages'] = !empty($_POST['feature_auto_link']);
$jsonContent = null; // reload from file $configData['features']['search_enabled'] = !empty($_POST['feature_search']);
} $configData['features']['breadcrumbs_enabled'] = !empty($_POST['feature_breadcrumbs']);
}
}
$siteConfig = file_exists($configJson) ? file_get_contents($configJson) : '{}'; file_put_contents($configJson, json_encode($configData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
if (isset($jsonContent)) { adminLog($config, 'info', $user['username'] . ' wijzigde site configuratie');
$siteConfig = $jsonContent; $message = 'Configuratie opgeslagen.';
$messageType = 'success';
}
} }
$route = 'config'; $route = 'config';