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:
2026-08-10 15:36:29 +02:00
parent 0961b23b8d
commit cd498c8c3a
209 changed files with 11033 additions and 9125 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+54
View File
@@ -0,0 +1,54 @@
/* Mobile search improvements */
@media (max-width: 991.98px) {
.navbar {
position: relative;
}
/* Hide desktop elements on mobile */
.d-none.d-lg-flex {
display: none !important;
}
#mobileSearch {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--header-bg);
z-index: 1030;
}
#mobileSearch.show {
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* Ensure navigation doesn't overlap with expanded search */
.navigation-section {
margin-top: 0;
border: none !important;
}
}
/* Desktop improvements */
@media (min-width: 992px) {
/* Hide mobile elements on desktop */
.d-lg-none {
display: none !important;
}
/* Hide mobile search on desktop */
#mobileSearch {
display: none !important;
}
}
/* Search form improvements */
.form-control[type="search"] {
min-width: 200px;
}
@media (max-width: 576px) {
.form-control[type="search"] {
min-width: 150px;
}
}
+4
View File
@@ -0,0 +1,4 @@
/*!
* Bootstrap v5.3.8 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+1
View File
@@ -0,0 +1 @@
1786274744
+266
View File
@@ -0,0 +1,266 @@
:root {
--header-bg: #613583;
--header-font: #ffffff;
--header-height: 120px;
--nav-bg: #813d9c;
--nav-font: #ffffff;
--nav-height: 50px;
--sidebar-bg: #f3e8f7;
--sidebar-border: #d8b4e0;
}
#site-header {
background-image: none;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
position: relative;
}
.navbar {
background-color: var(--header-bg) !important;
min-height: var(--header-height);
border-bottom: 4px solid var(--nav-bg) !important;
}
.navbar .navbar-brand,
.navbar .navbar-text,
.navbar .form-control,
.navbar .btn {
color: var(--header-font) !important;
}
.navbar .form-control::placeholder {
color: rgba(255, 255, 255, 0.7) !important;
}
.navbar .btn-outline-light {
border-color: var(--header-font) !important;
}
.dropdown-menu {
background-color: var(--header-bg) !important;
border: 1px solid var(--header-font) !important;
}
.dropdown-item {
color: var(--header-font) !important;
}
.dropdown-item:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
}
.dropdown-toggle::after {
display: none !important;
}
.btn-outline-light {
color: var(--header-font) !important;
border-color: var(--header-font) !important;
}
.btn-outline-light:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
}
.btn-outline-light:focus, .btn-outline-light:active, .show > .btn-outline-light.dropdown-toggle {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
border-color: var(--header-font) !important;
box-shadow: none !important;
}
.bi-chevron-down {
font-size: 0.75em;
margin-left: 0.25rem;
}
.breadcrumb-item + .breadcrumb-item::before {
content: "" !important;
padding: 0 !important;
}
.breadcrumb {
--bs-breadcrumb-divider: "";
--bs-breadcrumb-margin-bottom: 0;
margin-bottom: 0;
}
.breadcrumb .breadcrumb-item {
color: var(--header-bg) !important;
}
.breadcrumb .breadcrumb-item a {
color: var(--header-bg) !important;
text-decoration: none;
}
.breadcrumb .breadcrumb-item a:hover {
text-decoration: underline;
}
.sidebar-toggle-item {
display: flex;
align-items: center;
margin-right: 0.5rem;
}
.sidebar-toggle-btn {
padding: 0;
line-height: 1;
font-size: 1.1rem;
color: var(--header-bg) !important;
border: none !important;
background: transparent !important;
box-shadow: none !important;
cursor: pointer;
}
.sidebar-toggle-btn:hover {
opacity: 0.7;
}
.sidebar-column {
transition: all 0.3s ease;
}
.sidebar-hidden {
display: none !important;
}
.navigation-section {
background-color: var(--nav-bg) !important;
color: var(--nav-font) !important;
min-height: var(--nav-height);
}
.focus-visible:focus,
.btn:focus,
.form-control:focus,
.nav-link:focus {
outline: 3px solid #0056b3 !important;
outline-offset: 2px !important;
box-shadow: 0 0 0 1px #ffffff, 0 0 0 4px #0056b3 !important;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
.nav-tabs {
background-color: transparent !important;
border: none !important;
}
.nav-tabs .nav-link {
background-color: transparent !important;
border: none !important;
color: var(--nav-font) !important;
}
.nav-tabs .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
}
.nav-tabs .nav-link.active {
background-color: rgba(255, 255, 255, 0.2) !important;
border-bottom: 2px solid var(--nav-font) !important;
}
.sidebar-column {
background-color: var(--sidebar-bg) !important;
border-right: 1px solid var(--sidebar-border) !important;
position: sticky;
top: 0;
min-height: calc(100vh - var(--header-height) - var(--nav-height) - 42px);
}
.sidebar {
padding: 1.5rem;
height: 100%;
overflow-y: auto;
}
.sidebar .card {
border-radius: 1rem;
border: 1px solid var(--sidebar-border);
}
.content-column {
background-color: #ffffff;
}
.content-wrapper {
padding: 2rem;
padding-bottom: 80px !important;
}
.main-content {
flex: 1;
}
@media (max-width: 767.98px) {
.sidebar-column {
border-right: none !important;
border-top: 1px solid var(--sidebar-border) !important;
min-height: auto;
margin-top: 1rem;
}
.content-wrapper {
min-height: auto;
padding-bottom: 2rem !important;
}
}
@media (max-width: 991.98px) {
.sidebar-column {
order: 2 !important;
}
.content-column {
order: 1 !important;
}
}
pre {
background: #f8f9fa;
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
border: 1px solid #dee2e6;
margin-bottom: 1rem;
}
pre code {
background: none;
padding: 0;
color: #333;
font-size: 0.85rem;
line-height: 1.5;
}
code {
background: #e8e8e8;
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
color: #d63384;
}
.footer-icon {
color: #6c757d;
text-decoration: none;
transition: all 0.2s ease-in-out;
display: inline-block;
padding: 2px;
}
.footer-icon:hover {
color: #0d6efd;
transform: translateY(-1px);
}
.footer-icon:active {
transform: translateY(0);
}
.footer-icon.guide:hover {
color: #198754;
}
.footer-icon.cms:hover {
color: #dc3545;
}
.footer-icon.git:hover {
color: #6f42c1;
}
.footer-icon.website:hover {
color: #fd7e14;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<!-- < -->
<path d="M8 8 L3 16 L8 24" stroke="#ffffff" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- / -->
<path d="M12 24 L18 8" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
<!-- .. -->
<circle cx="22" cy="20" r="2" fill="#ffffff"/>
<circle cx="28" cy="20" r="2" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 442 B

+11
View File
@@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<!-- < -->
<path d="M8 8 L3 16 L8 24" stroke="#ffffff" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- / -->
<path d="M12 24 L18 8" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
<!-- .. -->
<circle cx="22" cy="20" r="2" fill="#ffffff"/>
<circle cx="28" cy="20" r="2" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 442 B

+94
View File
@@ -0,0 +1,94 @@
// Main application JavaScript
// This file contains general application functionality
/**
* Toggle sidebar visibility (open/close)
*/
function toggleSidebar() {
const sidebar = document.getElementById('site-sidebar');
const contentCol = sidebar ? sidebar.nextElementSibling || sidebar.parentElement.querySelector('.content-column') : null;
const btn = document.querySelector('.sidebar-toggle-btn');
const icon = btn ? btn.querySelector('i') : null;
if (!sidebar) return;
sidebar.classList.toggle('sidebar-hidden');
// Adjust content column width, toggle icon, and update aria-expanded
if (sidebar.classList.contains('sidebar-hidden')) {
if (contentCol) {
contentCol.classList.remove('col-lg-9', 'col-md-8');
contentCol.classList.add('col-12');
}
if (icon) {
icon.classList.remove('bi-layout-sidebar-inset');
icon.classList.add('bi-layout-sidebar');
}
if (btn) {
btn.setAttribute('aria-expanded', 'false');
}
sessionStorage.setItem('sidebarHidden', 'true');
} else {
if (contentCol) {
contentCol.classList.remove('col-12');
contentCol.classList.add('col-lg-9', 'col-md-8');
}
if (icon) {
icon.classList.remove('bi-layout-sidebar');
icon.classList.add('bi-layout-sidebar-inset');
}
if (btn) {
btn.setAttribute('aria-expanded', 'true');
}
sessionStorage.setItem('sidebarHidden', 'false');
}
}
/**
* Restore sidebar state from sessionStorage on page load
*/
function restoreSidebarState() {
if (sessionStorage.getItem('sidebarHidden') === 'true') {
const sidebar = document.getElementById('site-sidebar');
if (sidebar) {
toggleSidebar();
}
}
}
// Initialize application when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
// Restore sidebar state
restoreSidebarState();
// Handle nested dropdowns for touch devices using event delegation
document.addEventListener('click', function(e) {
const toggle = e.target.closest('.dropdown-submenu .dropdown-toggle');
if (toggle) {
e.preventDefault();
e.stopPropagation();
const submenu = toggle.closest('.dropdown-submenu');
const dropdown = submenu.querySelector('.dropdown-menu');
// Close other submenus at the same level
const parent = submenu.parentElement;
parent.querySelectorAll('.dropdown-submenu').forEach(function(sibling) {
if (sibling !== submenu) {
var siblingMenu = sibling.querySelector('.dropdown-menu');
if (siblingMenu) siblingMenu.classList.remove('show');
}
});
// Toggle current submenu
if (dropdown) dropdown.classList.toggle('show');
return;
}
// Close all open submenus when clicking outside
document.querySelectorAll('.dropdown-submenu .dropdown-menu.show').forEach(function(menu) {
menu.classList.remove('show');
});
});
});
File diff suppressed because one or more lines are too long
+333
View File
@@ -0,0 +1,333 @@
// CodePress demo theme styles
// A distinct look to demonstrate that themes are fully swappable.
// Theme color variables (defined per theme; used by the CSS custom properties below)
$header-bg: #613583;
$header-font: #ffffff;
$header-height: 120px;
$nav-bg: #813d9c;
$nav-font: #ffffff;
$nav-height: 50px;
$sidebar-bg: #f3e8f7;
$sidebar-border: #d8b4e0;
// Optional header background image and overlay opacity
$header-bg-image: none;
$header-bg-opacity: 1;
:root {
--header-bg: #{$header-bg};
--header-font: #{$header-font};
--header-height: #{$header-height};
--nav-bg: #{$nav-bg};
--nav-font: #{$nav-font};
--nav-height: #{$nav-height};
--sidebar-bg: #{$sidebar-bg};
--sidebar-border: #{$sidebar-border};
}
// Header background (optional background image via $header-bg-image)
#site-header {
background-image: $header-bg-image;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
position: relative;
}
@if $header-bg-image != 'none' {
#site-header::before {
content: '';
position: absolute;
inset: 0;
background-color: var(--header-bg);
opacity: $header-bg-opacity;
pointer-events: none;
z-index: 0;
}
}
// Rounded, softer header
.navbar {
background-color: var(--header-bg) !important;
min-height: var(--header-height);
border-bottom: 4px solid var(--nav-bg) !important;
.navbar-brand,
.navbar-text,
.form-control,
.btn {
color: var(--header-font) !important;
}
.form-control::placeholder {
color: rgba(255, 255, 255, 0.7) !important;
}
.btn-outline-light {
border-color: var(--header-font) !important;
}
}
// Language dropdown styling
.dropdown-menu {
background-color: var(--header-bg) !important;
border: 1px solid var(--header-font) !important;
}
.dropdown-item {
color: var(--header-font) !important;
&:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
}
}
.dropdown-toggle::after {
display: none !important;
}
.btn-outline-light {
color: var(--header-font) !important;
border-color: var(--header-font) !important;
&:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
}
&:focus,
&:active,
.show > &.dropdown-toggle {
background-color: rgba(255, 255, 255, 0.1) !important;
color: var(--header-font) !important;
border-color: var(--header-font) !important;
box-shadow: none !important;
}
}
.bi-chevron-down {
font-size: 0.75em;
margin-left: 0.25rem;
}
// Breadcrumb
.breadcrumb-item + .breadcrumb-item::before {
content: "" !important;
padding: 0 !important;
}
.breadcrumb {
--bs-breadcrumb-divider: "";
--bs-breadcrumb-margin-bottom: 0;
margin-bottom: 0;
.breadcrumb-item {
color: var(--header-bg) !important;
a {
color: var(--header-bg) !important;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
.sidebar-toggle-item {
display: flex;
align-items: center;
margin-right: 0.5rem;
}
.sidebar-toggle-btn {
padding: 0;
line-height: 1;
font-size: 1.1rem;
color: var(--header-bg) !important;
border: none !important;
background: transparent !important;
box-shadow: none !important;
cursor: pointer;
&:hover {
opacity: 0.7;
}
}
.sidebar-column {
transition: all 0.3s ease;
}
.sidebar-hidden {
display: none !important;
}
// Navigation section background
.navigation-section {
background-color: var(--nav-bg) !important;
color: var(--nav-font) !important;
min-height: var(--nav-height);
}
// Accessibility
.focus-visible:focus,
.btn:focus,
.form-control:focus,
.nav-link:focus {
outline: 3px solid #0056b3 !important;
outline-offset: 2px !important;
box-shadow: 0 0 0 1px #ffffff, 0 0 0 4px #0056b3 !important;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
.nav-tabs {
background-color: transparent !important;
border: none !important;
.nav-link {
background-color: transparent !important;
border: none !important;
color: var(--nav-font) !important;
&:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
}
&.active {
background-color: rgba(255, 255, 255, 0.2) !important;
border-bottom: 2px solid var(--nav-font) !important;
}
}
}
// Sidebar styling - rounded cards look
.sidebar-column {
background-color: var(--sidebar-bg) !important;
border-right: 1px solid var(--sidebar-border) !important;
position: sticky;
top: 0;
min-height: calc(100vh - var(--header-height) - var(--nav-height) - 42px);
}
.sidebar {
padding: 1.5rem;
height: 100%;
overflow-y: auto;
.card {
border-radius: 1rem;
border: 1px solid var(--sidebar-border);
}
}
.content-column {
background-color: #ffffff;
}
.content-wrapper {
padding: 2rem;
padding-bottom: 80px !important;
}
.main-content {
flex: 1;
}
// Mobile responsive
@media (max-width: 767.98px) {
.sidebar-column {
border-right: none !important;
border-top: 1px solid var(--sidebar-border) !important;
min-height: auto;
margin-top: 1rem;
}
.content-wrapper {
min-height: auto;
padding-bottom: 2rem !important;
}
}
@media (max-width: 991.98px) {
.sidebar-column {
order: 2 !important;
}
.content-column {
order: 1 !important;
}
}
// Code block styling
pre {
background: #f8f9fa;
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
border: 1px solid #dee2e6;
margin-bottom: 1rem;
code {
background: none;
padding: 0;
color: #333;
font-size: 0.85rem;
line-height: 1.5;
}
}
code {
background: #e8e8e8;
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
color: #d63384;
}
// Footer icon hover effects
.footer-icon {
color: #6c757d;
text-decoration: none;
transition: all 0.2s ease-in-out;
display: inline-block;
padding: 2px;
&:hover {
color: #0d6efd;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
&.guide:hover {
color: #198754;
}
&.cms:hover {
color: #dc3545;
}
&.git:hover {
color: #6f42c1;
}
&.website:hover {
color: #fd7e14;
}
}