v2.5.1: Admin theme refactor, Navigation plugin, user roles, guide restructure
- Reorganize admin into admin/theme/default/ (views + assets) - Rename GuideNav to Navigation plugin (essential, protected) - Plugin assets support (SCSS/CSS) loaded after theme CSS - User roles: Admin, Content Manager, BI Manager, Site Admin - Role-based access control (RBAC) for admin routes and sidebar - Guide restructure: sub-topics in separate folders with sidebar nav - Dynamic breadcrumb for homepage and subdirectories - Fix theme path traversal (../../ -> ../) in admin.php - Fix CodeMirror mode load order (xml -> css -> js -> htmlmixed -> php) - Fix editor-toolbar.js null checks for plugin edit pages - Layout select from theme.json with live frontmatter update - Footer sticky at bottom of viewport (min-height: 100vh) - Breadcrumb color fix (var(--nav-font) -> var(--header-bg)) - Remove language switcher from guide pages - Update README.md and README.en.md - Bump version to 2.5.1
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
{% extends "layouts/admin.twig" %}
|
||||
|
||||
{% block title %}{{ fileName }} - CodePress Admin{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mb-4"><i class="bi bi-pencil"></i> {{ fileName }}</h2>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/admin/content-edit?file={{ file|url_encode }}" id="editor-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label for="filename" class="form-label">Bestandsnaam</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="filename" name="filename" value="{{ fileName|replace({'.md': '', '.php': '', '.html': ''}) }}" required>
|
||||
<span class="input-group-text">.{{ fileExt }}</span>
|
||||
</div>
|
||||
<small class="form-text text-muted">Alleen letters, cijfers, punten, underscores en streepjes.</small>
|
||||
</div>
|
||||
{% if isEditable %}
|
||||
<div class="col-md-4">
|
||||
<label for="layout" class="form-label">Sjabloon / Layout <small class="text-muted">({{ activeThemeName|default('default') }} theme)</small></label>
|
||||
<select class="form-select" id="layout" name="layout">
|
||||
{% for key, layoutFile in themeLayouts %}
|
||||
<option value="{{ key }}" {{ currentLayout == key ? 'selected' : '' }}>{{ key|replace({'_': ' '})|capitalize }}{% if key == themeDefaultLayout %} (standaard){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% if availablePlugins is not empty %}
|
||||
<div class="col-md-4">
|
||||
<label class="form-label d-block">Zichtbare plugins</label>
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
{% for plugin in availablePlugins %}
|
||||
<input type="checkbox" class="btn-check" id="plugin-{{ plugin }}" name="plugins[]" value="{{ plugin }}" autocomplete="off" {{ plugin in selectedPlugins ? 'checked' : '' }}>
|
||||
<label class="btn btn-outline-primary btn-sm" for="plugin-{{ plugin }}">{{ plugin }}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if isEditable %}
|
||||
<div class="editor-toolbar" id="editor-toolbar"></div>
|
||||
<div class="editor-wrapper">
|
||||
<textarea name="content" id="editor-textarea" data-ext="{{ fileExt }}">{{ fileContent }}</textarea>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-primary" title="Opslaan (Ctrl+S)">
|
||||
<i class="bi bi-check-lg"></i> Opslaan
|
||||
</button>
|
||||
{% if isEditable %}
|
||||
<a href="/{{ currentLang }}{% if fileDir %}/{{ fileDir }}{% endif %}/{{ fileName|replace({'.md': '', '.php': '', '.html': ''}) }}" target="_blank" class="btn btn-outline-info" title="Open in nieuw tabblad">
|
||||
<i class="bi bi-eye"></i> Preview
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="/admin/content?dir={{ fileDir|url_encode }}" class="btn btn-outline-secondary" id="back-btn">Terug</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var backBtn = document.getElementById('back-btn');
|
||||
var filenameInput = document.getElementById('filename');
|
||||
var layoutSelect = document.getElementById('layout');
|
||||
if (!backBtn) return;
|
||||
|
||||
var changed = false;
|
||||
|
||||
function markChanged() {
|
||||
if (changed) return;
|
||||
changed = true;
|
||||
backBtn.innerHTML = '<i class="bi bi-x-circle"></i> Annuleren';
|
||||
backBtn.classList.remove('btn-outline-secondary');
|
||||
backBtn.classList.add('btn-outline-danger');
|
||||
}
|
||||
|
||||
if (filenameInput) {
|
||||
filenameInput.addEventListener('input', markChanged);
|
||||
}
|
||||
|
||||
// Update the layout value in the editor's frontmatter when the select changes
|
||||
if (layoutSelect) {
|
||||
layoutSelect.addEventListener('change', function () {
|
||||
var newLayout = this.value;
|
||||
var editor = document.getElementById('editor-textarea');
|
||||
if (!editor) return;
|
||||
|
||||
var cm = window.codeMirrorEditor;
|
||||
var content = cm ? cm.getValue() : editor.value;
|
||||
|
||||
// Check if frontmatter exists
|
||||
var fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (fmMatch) {
|
||||
// Update existing layout line in frontmatter
|
||||
var frontmatter = fmMatch[1];
|
||||
if (/^layout:\s*.+$/m.test(frontmatter)) {
|
||||
frontmatter = frontmatter.replace(/^layout:\s*.+$/m, 'layout: ' + newLayout);
|
||||
} else {
|
||||
frontmatter = 'layout: ' + newLayout + '\n' + frontmatter;
|
||||
}
|
||||
content = content.replace(/^---\n([\s\S]*?)\n---/, '---\n' + frontmatter + '\n---');
|
||||
} else {
|
||||
// No frontmatter yet — add one
|
||||
content = '---\nlayout: ' + newLayout + '\n---\n\n' + content;
|
||||
}
|
||||
|
||||
if (cm) {
|
||||
cm.setValue(content);
|
||||
} else {
|
||||
editor.value = content;
|
||||
}
|
||||
|
||||
markChanged();
|
||||
});
|
||||
}
|
||||
|
||||
window.__onContentChange = markChanged;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user