Fix guide template variable replacement and enhance documentation

- Fix template variable replacement in guide pages by removing {{}} brackets
- Escape code blocks in guide markdown to prevent template processing
- Completely rewrite guide documentation with comprehensive CMS features
- Add bilingual guide support (English/Dutch) with detailed examples
- Enhance CodePressCMS core with improved guide page handling
- Update template system with better layout and footer components
- Improve language files with additional translations
- Update configuration with enhanced theme and language settings

Resolves issue where guide pages were showing replaced template variables
instead of displaying them as documentation examples.
This commit is contained in:
2025-11-26 16:50:49 +01:00
parent f5ac28a74e
commit 9c5a43c5ce
17 changed files with 1684 additions and 462 deletions

View File

@@ -21,11 +21,12 @@
* @license MIT
*/
class CodePressCMS {
private $config;
public $config;
public $currentLanguage;
public $searchResults = [];
private $menu = [];
private $searchResults = [];
private $currentLanguage;
private $translations = [];
private $pluginManager;
/**
* Constructor - Initialize the CMS with configuration
@@ -43,6 +44,14 @@ class CodePressCMS {
$this->currentLanguage = $this->getCurrentLanguage();
$this->translations = $this->loadTranslations($this->currentLanguage);
// Initialize plugin manager
require_once __DIR__ . '/../plugin/PluginManager.php';
require_once __DIR__ . '/../plugin/CMSAPI.php';
$this->pluginManager = new PluginManager(__DIR__ . '/../../../plugins');
$api = new CMSAPI($this);
$this->pluginManager->setAPI($api);
$this->buildMenu();
if (isset($_GET['search'])) {
@@ -67,7 +76,7 @@ class CodePressCMS {
*
* @return array Available languages with their codes and names
*/
private function getAvailableLanguages() {
public function getAvailableLanguages() {
$langDir = __DIR__ . '/../../lang/';
$languages = [];
@@ -444,16 +453,59 @@ class CodePressCMS {
];
}
/**
* Parse metadata from content
*
* @param string $content Raw content
* @return array Parsed metadata and content without meta block
*/
private function parseMetadata($content) {
$metadata = [];
$contentWithoutMeta = $content;
// Check for YAML frontmatter (--- at start and end)
if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) {
$metaContent = $matches[1];
$contentWithoutMeta = $matches[2];
// Parse YAML-like metadata
$lines = explode("\n", $metaContent);
foreach ($lines as $line) {
if (strpos($line, ':') !== false) {
list($key, $value) = explode(':', $line, 2);
$key = trim($key);
$value = trim($value, ' "\'');
// Handle boolean values
if ($value === 'true') $value = true;
elseif ($value === 'false') $value = false;
$metadata[$key] = $value;
}
}
}
return [
'metadata' => $metadata,
'content' => $contentWithoutMeta
];
}
/**
* Parse Markdown content to HTML using League CommonMark
*
* @param string $content Raw Markdown content
* @return array Parsed content with title and body
*/
private function parseMarkdown($content, $actualFilePath = '') {
// Extract title from first H1
$title = '';
if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
public function parseMarkdown($content, $actualFilePath = '') {
// Parse metadata first
$parsed = $this->parseMetadata($content);
$metadata = $parsed['metadata'];
$content = $parsed['content'];
// Extract title from first H1 or metadata
$title = $metadata['title'] ?? '';
if (empty($title) && preg_match('/^#\s+(.+)$/m', $content, $matches)) {
$title = trim($matches[1]);
}
@@ -495,8 +547,10 @@ class CodePressCMS {
$body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
return [
'title' => $cleanName ?: 'Untitled',
'content' => $body
'title' => $title ?: $cleanName ?: 'Untitled',
'content' => $body,
'metadata' => $metadata,
'layout' => $metadata['layout'] ?? 'sidebar-content'
];
}
@@ -544,7 +598,7 @@ class CodePressCMS {
*
* @return array Associative array of page paths to titles
*/
private function getAllPageTitles() {
public function getAllPageTitles() {
$pages = [];
$this->scanForPageTitles($this->config['content_dir'], '', $pages);
return $pages;
@@ -708,19 +762,36 @@ class CodePressCMS {
* @return array Parsed content with title and body
*/
private function parsePHP($filePath) {
// Read file content first to extract metadata
$fileContent = file_get_contents($filePath);
$parsed = $this->parseMetadata($fileContent);
$metadata = $parsed['metadata'];
// Extract title from metadata or PHP variables
$title = $metadata['title'] ?? '';
ob_start();
// Make metadata available to the included file
$pageMetadata = $metadata;
include $filePath;
$content = ob_get_clean();
// Remove any remaining metadata from PHP output
$content = preg_replace('/^---\s*\n.*?\n---\s*\n/s', '', $content);
// Remove metadata from content if it was included
$parsed = $this->parseMetadata($content);
$content = $parsed['content'];
// Extract filename for title
$filename = basename($filePath);
$cleanName = $this->formatDisplayName($filename);
return [
'title' => $cleanName ?: 'Untitled',
'content' => $content
'title' => $title ?: $cleanName ?: 'Untitled',
'content' => $content,
'metadata' => $metadata,
'layout' => $metadata['layout'] ?? 'sidebar-content'
];
}
@@ -731,13 +802,30 @@ class CodePressCMS {
* @return array Parsed content with title and body
*/
private function parseHTML($content, $actualFilePath = '') {
// Parse metadata first
$parsed = $this->parseMetadata($content);
$metadata = $parsed['metadata'];
$content = $parsed['content'];
// Extract title from metadata or HTML tags
$title = $metadata['title'] ?? '';
if (empty($title)) {
if (preg_match('/<title>(.*?)<\/title>/i', $content, $matches)) {
$title = trim(strip_tags($matches[1]));
} elseif (preg_match('/<h1[^>]*>(.*?)<\/h1>/i', $content, $matches)) {
$title = trim(strip_tags($matches[1]));
}
}
// Extract filename for title
$filename = basename($actualFilePath);
$cleanName = $this->formatDisplayName($filename);
return [
'title' => $cleanName ?: 'Untitled',
'content' => $content
'title' => $title ?: $cleanName ?: 'Untitled',
'content' => $content,
'metadata' => $metadata,
'layout' => $metadata['layout'] ?? 'sidebar-content'
];
}
@@ -746,7 +834,7 @@ class CodePressCMS {
*
* @return bool True if content directory is empty or doesn't exist
*/
private function isContentDirEmpty() {
public function isContentDirEmpty() {
$contentDir = $this->config['content_dir'];
if (!is_dir($contentDir)) {
return true;
@@ -763,21 +851,60 @@ class CodePressCMS {
*
* @return array Guide page data
*/
private function getGuidePage() {
private function getGuidePage() {
$lang = $this->currentLanguage;
$guideFile = __DIR__ . '/../../../guide/' . $lang . '.codepress.md';
if (!file_exists($guideFile)) {
$guideFile = __DIR__ . '/../../../guide/en.codepress.md'; // Fallback to English
}
}
$content = file_get_contents($guideFile);
$result = $this->parseMarkdown($content);
// Parse metadata first
$parsed = $this->parseMetadata($content);
$metadata = $parsed['metadata'];
$contentWithoutMeta = $parsed['content'];
// Include autoloader
require_once __DIR__ . '/../../../vendor/autoload.php';
// Configure CommonMark environment
$config = [
'html_input' => 'strip',
'allow_unsafe_links' => false,
'max_nesting_level' => 100,
];
// Create environment with extensions
$environment = new \League\CommonMark\Environment\Environment($config);
$environment->addExtension(new \League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension());
$environment->addExtension(new \League\CommonMark\Extension\Autolink\AutolinkExtension());
$environment->addExtension(new \League\CommonMark\Extension\Strikethrough\StrikethroughExtension());
$environment->addExtension(new \League\CommonMark\Extension\Table\TableExtension());
$environment->addExtension(new \League\CommonMark\Extension\TaskList\TaskListExtension());
// Create converter
$converter = new \League\CommonMark\MarkdownConverter($environment);
// Convert to HTML
$body = $converter->convert($contentWithoutMeta)->getContent();
// Extract title from metadata or first H1
$title = $metadata['title'] ?? '';
if (empty($title) && preg_match('/^#\s+(.+)$/m', $contentWithoutMeta, $matches)) {
$title = trim($matches[1]);
}
// Set special title for guide
$result['title'] = $this->t('manual') . ' - CodePress CMS';
$title = $this->t('manual') . ' - CodePress CMS';
return $result;
return [
'title' => $title,
'content' => $body,
'metadata' => $metadata,
'layout' => $metadata['layout'] ?? 'content'
];
}
/**
@@ -912,12 +1039,21 @@ class CodePressCMS {
// Get homepage title
$homepageTitle = $this->getHomepageTitle();
// Get sidebar content from plugins
$sidebarContent = $this->pluginManager->getSidebarContent();
// Get layout from page metadata
$layout = $page['layout'] ?? 'sidebar-content';
// Prepare template data
$templateData = [
'site_title' => $this->config['site_title'],
'page_title' => htmlspecialchars($page['title']),
'content' => $page['content'],
'sidebar_content' => $sidebarContent,
'layout' => $layout,
'page_metadata' => $page['metadata'] ?? [],
'search_query' => isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '',
'menu' => $this->renderMenu($menu),
'breadcrumb' => $breadcrumb,
@@ -930,7 +1066,7 @@ class CodePressCMS {
'lang_switch_url' => isset($_GET['guide']) ? '&guide' : '&page=' . $this->config['default_page'],
'author_name' => $this->config['author']['name'] ?? 'CodePress Developer',
'author_website' => $this->config['author']['website'] ?? '#',
'author_git' => $this->config['author']['git'] ?? '#',
'author_git' => 'https://git.noorlander.info/E.Noorlander',
'seo_description' => $this->config['seo']['description'] ?? 'CodePress CMS - Lightweight file-based content management system',
'seo_keywords' => $this->config['seo']['keywords'] ?? 'cms, php, content management, file-based',
'cms_version' => isset($this->config['version_info']) ? 'v' . $this->config['version_info']['version'] : '',
@@ -939,6 +1075,8 @@ class CodePressCMS {
'header_font_color' => $this->config['theme']['header_font_color'] ?? '#ffffff',
'navigation_color' => $this->config['theme']['navigation_color'] ?? '#f8f9fa',
'navigation_font_color' => $this->config['theme']['navigation_font_color'] ?? '#000000',
'sidebar_background' => $this->config['theme']['sidebar_background'] ?? '#f8f9fa',
'sidebar_border' => $this->config['theme']['sidebar_border'] ?? '#dee2e6',
// Language
'current_lang' => $this->currentLanguage,
'current_lang_upper' => strtoupper($this->currentLanguage),
@@ -967,17 +1105,20 @@ class CodePressCMS {
't_page_not_found' => $this->t('page_not_found'),
't_page_not_found_text' => $this->t('page_not_found_text'),
't_mappen' => $this->t('mappen'),
't_paginas' => $this->t('paginas')
't_paginas' => $this->t('paginas'),
't_author_website' => $this->t('author_website'),
't_author_git' => $this->t('author_git')
];
// File info for footer
if (isset($page['file_info'])) {
$templateData['file_info'] = $this->t('created') . ': ' . htmlspecialchars($page['file_info']['created']) .
' | ' . $this->t('modified') . ': ' . htmlspecialchars($page['file_info']['modified']);
$templateData['file_info_block'] = '<span class="file-details"> | ' . $templateData['file_info'] . '</span>';
$templateData['created'] = htmlspecialchars($page['file_info']['created']);
$templateData['modified'] = htmlspecialchars($page['file_info']['modified']);
$templateData['file_info_block'] = true;
} else {
$templateData['file_info'] = '';
$templateData['file_info_block'] = '';
$templateData['created'] = '';
$templateData['modified'] = '';
$templateData['file_info_block'] = false;
}
@@ -1025,7 +1166,7 @@ class CodePressCMS {
*
* @return string Breadcrumb HTML
*/
private function generateBreadcrumb() {
public function generateBreadcrumb() {
if (isset($_GET['search'])) {
return '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="?page=' . $this->config['default_page'] . '&lang=' . $this->currentLanguage . '"></a></li><li class="breadcrumb-item"> > </li><li class="breadcrumb-item active">' . $this->t('search') . '</li></ol></nav>';
}

View File

@@ -37,6 +37,16 @@ class SimpleTemplate {
// Handle partial includes first ({{>partial}})
$template = preg_replace_callback('/{{>([^}]+)}}/', [$this, 'replacePartial'], $template);
// Handle equal conditionals first
$template = preg_replace_callback('/{{#equal\s+(\w+)\s+["\']([^"\']+)["\']}}(.*?){{\/equal}}/s', function($matches) {
$key = $matches[1];
$expectedValue = $matches[2];
$content = $matches[3];
$actualValue = $this->data[$key] ?? '';
return ($actualValue === $expectedValue) ? $content : '';
}, $template);
// Handle conditional blocks
foreach ($this->data as $key => $value) {
if (is_array($value)) {

View File

@@ -0,0 +1,216 @@
<?php
class CMSAPI
{
private CodePressCMS $cms;
public function __construct(CodePressCMS $cms)
{
$this->cms = $cms;
}
/**
* Get current page information
*/
public function getCurrentPage(): array
{
return $this->cms->getPage();
}
/**
* Get current page title
*/
public function getCurrentPageTitle(): string
{
$page = $this->cms->getPage();
return $page['title'] ?? '';
}
/**
* Get current page content
*/
public function getCurrentPageContent(): string
{
$page = $this->cms->getPage();
return $page['content'] ?? '';
}
/**
* Get current page URL
*/
public function getCurrentPageUrl(): string
{
$page = $_GET['page'] ?? $this->cms->config['default_page'];
$lang = $_GET['lang'] ?? $this->cms->config['language']['default'] ?? 'nl';
return "?page={$page}&lang={$lang}";
}
/**
* Get menu structure
*/
public function getMenu(): array
{
return $this->cms->getMenu();
}
/**
* Get configuration value
*/
public function getConfig(string $key, $default = null)
{
$keys = explode('.', $key);
$value = $this->cms->config;
foreach ($keys as $k) {
if (!isset($value[$k])) {
return $default;
}
$value = $value[$k];
}
return $value;
}
/**
* Get translation
*/
public function translate(string $key): string
{
return $this->cms->t($key);
}
/**
* Get current language
*/
public function getCurrentLanguage(): string
{
return $this->cms->currentLanguage;
}
/**
* Check if user is on homepage
*/
public function isHomepage(): bool
{
$defaultPage = $this->cms->config['default_page'] ?? 'index';
$currentPage = $_GET['page'] ?? $defaultPage;
return $currentPage === $defaultPage;
}
/**
* Get file info for current page
*/
public function getCurrentPageFileInfo(): ?array
{
$page = $this->cms->getPage();
return $page['file_info'] ?? null;
}
/**
* Get breadcrumb data
*/
public function getBreadcrumb(): string
{
return $this->cms->generateBreadcrumb();
}
/**
* Check if content directory has content
*/
public function hasContent(): bool
{
return !$this->cms->isContentDirEmpty();
}
/**
* Get search results if searching
*/
public function getSearchResults(): array
{
if (isset($_GET['search'])) {
return $this->cms->searchResults;
}
return [];
}
/**
* Check if currently searching
*/
public function isSearching(): bool
{
return isset($_GET['search']);
}
/**
* Get available languages
*/
public function getAvailableLanguages(): array
{
return $this->cms->getAvailableLanguages();
}
/**
* Create URL for page
*/
public function createUrl(string $page, ?string $lang = null): string
{
$lang = $lang ?? $this->getCurrentLanguage();
return "?page={$page}&lang={$lang}";
}
/**
* Execute PHP file and capture output
*/
public function executePhpFile(string $filePath): string
{
if (!file_exists($filePath)) {
return '';
}
ob_start();
include $filePath;
return ob_get_clean();
}
/**
* Get content from PHP/HTML/Markdown file
*/
public function getFileContent(string $filePath): string
{
if (!file_exists($filePath)) {
return '';
}
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
switch ($extension) {
case 'php':
return $this->executePhpFile($filePath);
case 'md':
$content = file_get_contents($filePath);
$result = $this->cms->parseMarkdown($content, $filePath);
return $result['content'] ?? '';
case 'html':
return file_get_contents($filePath);
default:
return file_get_contents($filePath);
}
}
/**
* Check if file exists in content directory
*/
public function contentFileExists(string $filename): bool
{
$contentDir = $this->cms->config['content_dir'];
return file_exists($contentDir . '/' . $filename);
}
/**
* Get all pages with their metadata
*/
public function getAllPages(): array
{
return $this->cms->getAllPageTitles();
}
}

View File

@@ -0,0 +1,77 @@
<?php
class PluginManager
{
private array $plugins = [];
private string $pluginsPath;
private ?CMSAPI $api = null;
public function __construct(string $pluginsPath)
{
$this->pluginsPath = $pluginsPath;
$this->loadPlugins();
}
public function setAPI(CMSAPI $api): void
{
$this->api = $api;
// Inject API into all plugins that have setAPI method
foreach ($this->plugins as $plugin) {
if (method_exists($plugin, 'setAPI')) {
$plugin->setAPI($api);
}
}
}
private function loadPlugins(): void
{
if (!is_dir($this->pluginsPath)) {
return;
}
$pluginDirs = glob($this->pluginsPath . '/*', GLOB_ONLYDIR);
foreach ($pluginDirs as $pluginDir) {
$pluginName = basename($pluginDir);
$pluginFile = $pluginDir . '/' . $pluginName . '.php';
if (file_exists($pluginFile)) {
require_once $pluginFile;
$className = $pluginName;
if (class_exists($className)) {
$this->plugins[$pluginName] = new $className();
// Inject API if already available
if ($this->api && method_exists($this->plugins[$pluginName], 'setAPI')) {
$this->plugins[$pluginName]->setAPI($this->api);
}
}
}
}
}
public function getPlugin(string $name): ?object
{
return $this->plugins[$name] ?? null;
}
public function getAllPlugins(): array
{
return $this->plugins;
}
public function getSidebarContent(): string
{
$sidebarContent = '';
foreach ($this->plugins as $plugin) {
if (method_exists($plugin, 'getSidebarContent')) {
$sidebarContent .= $plugin->getSidebarContent();
}
}
return $sidebarContent;
}
}

View File

@@ -22,5 +22,18 @@ return [
'page_not_found' => 'Page Not Found',
'page_not_found_text' => 'The page you are looking for does not exist.',
'mappen' => 'Folders',
'paginas' => 'Pages'
'paginas' => 'Pages',
'author_website' => 'Author website',
'author_git' => 'Author Git',
'plugins' => 'Plugins',
'templates' => 'Templates',
'layouts' => 'Layouts',
'sidebar_content' => 'Sidebar + Content',
'content_only' => 'Content Only',
'sidebar_only' => 'Sidebar Only',
'content_sidebar' => 'Content + Sidebar',
'plugin_development' => 'Plugin Development',
'template_system' => 'Template System',
'mqtt_tracking' => 'MQTT Tracking',
'real_time_analytics' => 'Real-time Analytics'
];

View File

@@ -22,5 +22,18 @@ return [
'page_not_found' => 'Pagina niet gevonden',
'page_not_found_text' => 'De pagina die u zoekt bestaat niet.',
'mappen' => 'Mappen',
'paginas' => 'Pagina\'s'
'paginas' => 'Pagina\'s',
'author_website' => 'Auteur website',
'author_git' => 'Auteur Git',
'plugins' => 'Plugins',
'templates' => 'Templates',
'layouts' => 'Layouts',
'sidebar_content' => 'Sidebar + Content',
'content_only' => 'Alleen Content',
'sidebar_only' => 'Alleen Sidebar',
'content_sidebar' => 'Content + Sidebar',
'plugin_development' => 'Plugin Ontwikkeling',
'template_system' => 'Template Systeem',
'mqtt_tracking' => 'MQTT Tracking',
'real_time_analytics' => 'Real-time Analytics'
];

View File

@@ -1,20 +1,39 @@
<footer class="bg-light border-top py-3">
<footer class="bg-light border-top py-1">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="d-flex justify-content-between align-items-center">
<div class="file-info">
<i class="bi bi-file-text"></i>
<span class="page-title" title="{{page_title}}">{{page_title}}</span>
{{{file_info_block}}}
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center">
<div class="file-info mb-2 mb-md-0">
<small class="text-muted">
<i class="bi bi-file-text footer-icon" title="{{t_file_details}}: {{page_title}}"></i>
<span class="page-title d-none d-lg-inline" title="{{page_title}}">{{page_title}}</span>
{{#file_info_block}}
<span class="ms-2">
<i class="bi bi-calendar-plus footer-icon" title="{{t_created}}: {{created}}"></i>
<span class="file-created">{{created}}</span>
<i class="bi bi-calendar-check ms-1 footer-icon" title="{{t_modified}}: {{modified}}"></i>
<span class="file-modified">{{modified}}</span>
</span>
{{/file_info_block}}
</small>
</div>
<div class="site-info">
<small class="text-muted">
<a href="?guide&lang={{current_lang}}" class="guide-link" title="{{t_guide}}">
<a href="?guide&lang={{current_lang}}" class="footer-icon guide" title="{{t_guide}}">
<i class="bi bi-book"></i>
</a>
<span class="ms-2">|</span>
{{t_powered_by}} <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a> {{cms_version}}
<span class="ms-1">|</span>
<a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener" class="footer-icon cms" title="{{t_powered_by}} CodePress CMS {{cms_version}}">
<i class="bi bi-cpu"></i>
</a>
<span class="ms-1">|</span>
<a href="{{author_website}}" target="_blank" rel="noopener" class="footer-icon website" title="{{t_author_website}}">
<i class="bi bi-globe"></i>
</a>
<span class="ms-1">|</span>
<a href="{{author_git}}" target="_blank" rel="noopener" class="footer-icon git" title="{{t_author_git}}">
<i class="bi bi-git"></i>
</a>
</small>
</div>
</div>

View File

@@ -34,6 +34,8 @@
--header-font: {{header_font_color}};
--nav-bg: {{navigation_color}};
--nav-font: {{navigation_font_color}};
--sidebar-bg: {{sidebar_background}};
--sidebar-border: {{sidebar_border}};
}
/* Header styles */
@@ -151,14 +153,112 @@
background-color: rgba(255,255,255,0.2) !important;
border-bottom: 2px solid var(--nav-font) !important;
}
/* Sidebar styling */
.sidebar-column {
background-color: var(--sidebar-bg) !important;
border-right: 1px solid var(--sidebar-border) !important;
position: sticky;
top: 0;
height: 100vh;
}
.sidebar {
padding: 1.5rem;
height: 100%;
overflow-y: auto;
}
.content-column {
background-color: #ffffff;
}
.content-wrapper {
padding: 2rem;
padding-bottom: 80px !important;
}
/* Ensure full height layout */
.main-content {
min-height: calc(100vh - 200px);
}
/* 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-column {
background-color: #ffffff;
}
.content-wrapper {
min-height: auto;
padding-bottom: 2rem !important;
}
}
/* Tablet and mobile: sidebar below content */
@media (max-width: 991.98px) {
.sidebar-column {
order: 2 !important;
}
.content-column {
order: 1 !important;
}
}
/* Footer icon hover effects */
.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);
}
/* Specific icon hover colors */
.footer-icon.guide:hover {
color: #198754;
}
.footer-icon.cms:hover {
color: #dc3545;
}
.footer-icon.git:hover {
color: #6f42c1;
}
.footer-icon.website:hover {
color: #fd7e14;
}
</style>
</head>
<body>
{{>header}}
<header id="site-header">
{{>header}}
</header>
{{>navigation}}
<nav id="site-navigation">
{{>navigation}}
</nav>
<div class="breadcrumb-section bg-light border-bottom">
<div id="site-breadcrumb" class="breadcrumb-section bg-light border-bottom">
<div class="container-fluid">
<div class="row">
<div class="col-12 py-2">
@@ -168,17 +268,87 @@
</div>
</div>
<div class="container-fluid main-content" style="padding-bottom: 80px;">
<div class="container">
<div class="row">
<main class="col-12">
{{>content_template}}
</main>
<main id="site-main" class="main-content" style="padding: 0;">
{{#sidebar_content}}
{{#equal layout "sidebar-content"}}
<div class="row g-0">
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column order-2 order-md-1">
<div class="sidebar h-100">
{{{sidebar_content}}}
</div>
</aside>
<section id="site-content" class="col-lg-9 col-md-8 content-column order-1 order-md-2">
<div class="content-wrapper p-4">
{{>content_template}}
</div>
</section>
</div>
{{/equal}}
{{#equal layout "content"}}
<div class="container">
<section id="site-content" class="col-12">
<div class="content-wrapper p-4">
{{>content_template}}
</div>
</section>
</div>
{{/equal}}
{{#equal layout "sidebar"}}
<div class="container-fluid">
<aside id="site-sidebar" class="col-12 sidebar-column">
<div class="sidebar">
{{{sidebar_content}}}
</div>
</aside>
</div>
{{/equal}}
{{#equal layout "content-sidebar"}}
<div class="row g-0">
<section id="site-content" class="col-lg-9 col-md-8 content-column order-1">
<div class="content-wrapper p-4">
{{>content_template}}
</div>
</section>
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column order-2">
<div class="sidebar h-100">
{{{sidebar_content}}}
</div>
</aside>
</div>
{{/equal}}
{{#equal layout "content-sidebar-reverse"}}
<div class="row g-0 flex-row-reverse">
<section id="site-content" class="col-lg-9 col-md-8 content-column">
<div class="content-wrapper p-4">
{{>content_template}}
</div>
</section>
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column">
<div class="sidebar h-100">
{{{sidebar_content}}}
</div>
</aside>
</div>
{{/equal}}
{{/sidebar_content}}
{{^sidebar_content}}
<div class="container">
<section id="site-content" class="col-12">
<div class="content-wrapper p-4">
{{>content_template}}
</div>
</section>
</div>
</div>
{{/sidebar_content}}
</main>
{{>footer}}
<footer id="site-footer">
{{>footer}}
</footer>
<script src="/assets/js/bootstrap.bundle.min.js"></script>
<script src="/assets/js/app.js"></script>