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:
parent
f5ac28a74e
commit
9c5a43c5ce
@ -8,7 +8,9 @@
|
|||||||
"header_color": "#0a369d",
|
"header_color": "#0a369d",
|
||||||
"header_font_color": "#ffffff",
|
"header_font_color": "#ffffff",
|
||||||
"navigation_color": "#2754b4",
|
"navigation_color": "#2754b4",
|
||||||
"navigation_font_color": "#ffffff"
|
"navigation_font_color": "#ffffff",
|
||||||
|
"sidebar_background": "#f8f9fa",
|
||||||
|
"sidebar_border": "#dee2e6"
|
||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"default": "nl",
|
"default": "nl",
|
||||||
@ -19,9 +21,8 @@
|
|||||||
"keywords": "cms, php, content management, file-based"
|
"keywords": "cms, php, content management, file-based"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Edwin Noorlander",
|
"name": "E. Noorlander",
|
||||||
"website": "https://noorlander.info",
|
"website": "https://noorlander.info"
|
||||||
"git": "https://git.noorlander.info/E.Noorlander/CodePress.git"
|
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
"auto_link_pages": true,
|
"auto_link_pages": true,
|
||||||
|
|||||||
@ -21,11 +21,12 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
class CodePressCMS {
|
class CodePressCMS {
|
||||||
private $config;
|
public $config;
|
||||||
|
public $currentLanguage;
|
||||||
|
public $searchResults = [];
|
||||||
private $menu = [];
|
private $menu = [];
|
||||||
private $searchResults = [];
|
|
||||||
private $currentLanguage;
|
|
||||||
private $translations = [];
|
private $translations = [];
|
||||||
|
private $pluginManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor - Initialize the CMS with configuration
|
* Constructor - Initialize the CMS with configuration
|
||||||
@ -43,6 +44,14 @@ class CodePressCMS {
|
|||||||
|
|
||||||
$this->currentLanguage = $this->getCurrentLanguage();
|
$this->currentLanguage = $this->getCurrentLanguage();
|
||||||
$this->translations = $this->loadTranslations($this->currentLanguage);
|
$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();
|
$this->buildMenu();
|
||||||
|
|
||||||
if (isset($_GET['search'])) {
|
if (isset($_GET['search'])) {
|
||||||
@ -67,7 +76,7 @@ class CodePressCMS {
|
|||||||
*
|
*
|
||||||
* @return array Available languages with their codes and names
|
* @return array Available languages with their codes and names
|
||||||
*/
|
*/
|
||||||
private function getAvailableLanguages() {
|
public function getAvailableLanguages() {
|
||||||
$langDir = __DIR__ . '/../../lang/';
|
$langDir = __DIR__ . '/../../lang/';
|
||||||
$languages = [];
|
$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
|
* Parse Markdown content to HTML using League CommonMark
|
||||||
*
|
*
|
||||||
* @param string $content Raw Markdown content
|
* @param string $content Raw Markdown content
|
||||||
* @return array Parsed content with title and body
|
* @return array Parsed content with title and body
|
||||||
*/
|
*/
|
||||||
private function parseMarkdown($content, $actualFilePath = '') {
|
public function parseMarkdown($content, $actualFilePath = '') {
|
||||||
// Extract title from first H1
|
// Parse metadata first
|
||||||
$title = '';
|
$parsed = $this->parseMetadata($content);
|
||||||
if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
|
$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]);
|
$title = trim($matches[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,8 +547,10 @@ class CodePressCMS {
|
|||||||
$body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
|
$body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'title' => $cleanName ?: 'Untitled',
|
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||||
'content' => $body
|
'content' => $body,
|
||||||
|
'metadata' => $metadata,
|
||||||
|
'layout' => $metadata['layout'] ?? 'sidebar-content'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +598,7 @@ class CodePressCMS {
|
|||||||
*
|
*
|
||||||
* @return array Associative array of page paths to titles
|
* @return array Associative array of page paths to titles
|
||||||
*/
|
*/
|
||||||
private function getAllPageTitles() {
|
public function getAllPageTitles() {
|
||||||
$pages = [];
|
$pages = [];
|
||||||
$this->scanForPageTitles($this->config['content_dir'], '', $pages);
|
$this->scanForPageTitles($this->config['content_dir'], '', $pages);
|
||||||
return $pages;
|
return $pages;
|
||||||
@ -708,19 +762,36 @@ class CodePressCMS {
|
|||||||
* @return array Parsed content with title and body
|
* @return array Parsed content with title and body
|
||||||
*/
|
*/
|
||||||
private function parsePHP($filePath) {
|
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();
|
ob_start();
|
||||||
|
// Make metadata available to the included file
|
||||||
|
$pageMetadata = $metadata;
|
||||||
include $filePath;
|
include $filePath;
|
||||||
$content = ob_get_clean();
|
$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
|
// Extract filename for title
|
||||||
$filename = basename($filePath);
|
$filename = basename($filePath);
|
||||||
$cleanName = $this->formatDisplayName($filename);
|
$cleanName = $this->formatDisplayName($filename);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'title' => $cleanName ?: 'Untitled',
|
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||||
'content' => $content
|
'content' => $content,
|
||||||
|
'metadata' => $metadata,
|
||||||
|
'layout' => $metadata['layout'] ?? 'sidebar-content'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,13 +802,30 @@ class CodePressCMS {
|
|||||||
* @return array Parsed content with title and body
|
* @return array Parsed content with title and body
|
||||||
*/
|
*/
|
||||||
private function parseHTML($content, $actualFilePath = '') {
|
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
|
// Extract filename for title
|
||||||
$filename = basename($actualFilePath);
|
$filename = basename($actualFilePath);
|
||||||
$cleanName = $this->formatDisplayName($filename);
|
$cleanName = $this->formatDisplayName($filename);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'title' => $cleanName ?: 'Untitled',
|
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||||
'content' => $content
|
'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
|
* @return bool True if content directory is empty or doesn't exist
|
||||||
*/
|
*/
|
||||||
private function isContentDirEmpty() {
|
public function isContentDirEmpty() {
|
||||||
$contentDir = $this->config['content_dir'];
|
$contentDir = $this->config['content_dir'];
|
||||||
if (!is_dir($contentDir)) {
|
if (!is_dir($contentDir)) {
|
||||||
return true;
|
return true;
|
||||||
@ -772,12 +860,51 @@ class CodePressCMS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$content = file_get_contents($guideFile);
|
$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
|
// 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
|
// Get homepage title
|
||||||
$homepageTitle = $this->getHomepageTitle();
|
$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
|
// Prepare template data
|
||||||
$templateData = [
|
$templateData = [
|
||||||
'site_title' => $this->config['site_title'],
|
'site_title' => $this->config['site_title'],
|
||||||
'page_title' => htmlspecialchars($page['title']),
|
'page_title' => htmlspecialchars($page['title']),
|
||||||
|
|
||||||
'content' => $page['content'],
|
'content' => $page['content'],
|
||||||
|
'sidebar_content' => $sidebarContent,
|
||||||
|
'layout' => $layout,
|
||||||
|
'page_metadata' => $page['metadata'] ?? [],
|
||||||
'search_query' => isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '',
|
'search_query' => isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '',
|
||||||
'menu' => $this->renderMenu($menu),
|
'menu' => $this->renderMenu($menu),
|
||||||
'breadcrumb' => $breadcrumb,
|
'breadcrumb' => $breadcrumb,
|
||||||
@ -930,7 +1066,7 @@ class CodePressCMS {
|
|||||||
'lang_switch_url' => isset($_GET['guide']) ? '&guide' : '&page=' . $this->config['default_page'],
|
'lang_switch_url' => isset($_GET['guide']) ? '&guide' : '&page=' . $this->config['default_page'],
|
||||||
'author_name' => $this->config['author']['name'] ?? 'CodePress Developer',
|
'author_name' => $this->config['author']['name'] ?? 'CodePress Developer',
|
||||||
'author_website' => $this->config['author']['website'] ?? '#',
|
'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_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',
|
'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'] : '',
|
'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',
|
'header_font_color' => $this->config['theme']['header_font_color'] ?? '#ffffff',
|
||||||
'navigation_color' => $this->config['theme']['navigation_color'] ?? '#f8f9fa',
|
'navigation_color' => $this->config['theme']['navigation_color'] ?? '#f8f9fa',
|
||||||
'navigation_font_color' => $this->config['theme']['navigation_font_color'] ?? '#000000',
|
'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
|
// Language
|
||||||
'current_lang' => $this->currentLanguage,
|
'current_lang' => $this->currentLanguage,
|
||||||
'current_lang_upper' => strtoupper($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' => $this->t('page_not_found'),
|
||||||
't_page_not_found_text' => $this->t('page_not_found_text'),
|
't_page_not_found_text' => $this->t('page_not_found_text'),
|
||||||
't_mappen' => $this->t('mappen'),
|
'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
|
// File info for footer
|
||||||
if (isset($page['file_info'])) {
|
if (isset($page['file_info'])) {
|
||||||
$templateData['file_info'] = $this->t('created') . ': ' . htmlspecialchars($page['file_info']['created']) .
|
$templateData['created'] = htmlspecialchars($page['file_info']['created']);
|
||||||
' | ' . $this->t('modified') . ': ' . htmlspecialchars($page['file_info']['modified']);
|
$templateData['modified'] = htmlspecialchars($page['file_info']['modified']);
|
||||||
$templateData['file_info_block'] = '<span class="file-details"> | ' . $templateData['file_info'] . '</span>';
|
$templateData['file_info_block'] = true;
|
||||||
} else {
|
} else {
|
||||||
$templateData['file_info'] = '';
|
$templateData['created'] = '';
|
||||||
$templateData['file_info_block'] = '';
|
$templateData['modified'] = '';
|
||||||
|
$templateData['file_info_block'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1025,7 +1166,7 @@ class CodePressCMS {
|
|||||||
*
|
*
|
||||||
* @return string Breadcrumb HTML
|
* @return string Breadcrumb HTML
|
||||||
*/
|
*/
|
||||||
private function generateBreadcrumb() {
|
public function generateBreadcrumb() {
|
||||||
if (isset($_GET['search'])) {
|
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>';
|
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>';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,16 @@ class SimpleTemplate {
|
|||||||
// Handle partial includes first ({{>partial}})
|
// Handle partial includes first ({{>partial}})
|
||||||
$template = preg_replace_callback('/{{>([^}]+)}}/', [$this, 'replacePartial'], $template);
|
$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
|
// Handle conditional blocks
|
||||||
foreach ($this->data as $key => $value) {
|
foreach ($this->data as $key => $value) {
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
|
|||||||
216
engine/core/plugin/CMSAPI.php
Normal file
216
engine/core/plugin/CMSAPI.php
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
77
engine/core/plugin/PluginManager.php
Normal file
77
engine/core/plugin/PluginManager.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,5 +22,18 @@ return [
|
|||||||
'page_not_found' => 'Page Not Found',
|
'page_not_found' => 'Page Not Found',
|
||||||
'page_not_found_text' => 'The page you are looking for does not exist.',
|
'page_not_found_text' => 'The page you are looking for does not exist.',
|
||||||
'mappen' => 'Folders',
|
'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'
|
||||||
];
|
];
|
||||||
@ -22,5 +22,18 @@ return [
|
|||||||
'page_not_found' => 'Pagina niet gevonden',
|
'page_not_found' => 'Pagina niet gevonden',
|
||||||
'page_not_found_text' => 'De pagina die u zoekt bestaat niet.',
|
'page_not_found_text' => 'De pagina die u zoekt bestaat niet.',
|
||||||
'mappen' => 'Mappen',
|
'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'
|
||||||
];
|
];
|
||||||
@ -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="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center">
|
||||||
<div class="file-info">
|
<div class="file-info mb-2 mb-md-0">
|
||||||
<i class="bi bi-file-text"></i>
|
<small class="text-muted">
|
||||||
<span class="page-title" title="{{page_title}}">{{page_title}}</span>
|
<i class="bi bi-file-text footer-icon" title="{{t_file_details}}: {{page_title}}"></i>
|
||||||
{{{file_info_block}}}
|
<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>
|
||||||
<div class="site-info">
|
<div class="site-info">
|
||||||
<small class="text-muted">
|
<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>
|
<i class="bi bi-book"></i>
|
||||||
</a>
|
</a>
|
||||||
<span class="ms-2">|</span>
|
<span class="ms-1">|</span>
|
||||||
{{t_powered_by}} <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a> {{cms_version}}
|
<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>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -34,6 +34,8 @@
|
|||||||
--header-font: {{header_font_color}};
|
--header-font: {{header_font_color}};
|
||||||
--nav-bg: {{navigation_color}};
|
--nav-bg: {{navigation_color}};
|
||||||
--nav-font: {{navigation_font_color}};
|
--nav-font: {{navigation_font_color}};
|
||||||
|
--sidebar-bg: {{sidebar_background}};
|
||||||
|
--sidebar-border: {{sidebar_border}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header styles */
|
/* Header styles */
|
||||||
@ -151,14 +153,112 @@
|
|||||||
background-color: rgba(255,255,255,0.2) !important;
|
background-color: rgba(255,255,255,0.2) !important;
|
||||||
border-bottom: 2px solid var(--nav-font) !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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<header id="site-header">
|
||||||
{{>header}}
|
{{>header}}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<nav id="site-navigation">
|
||||||
{{>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="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 py-2">
|
<div class="col-12 py-2">
|
||||||
@ -168,17 +268,87 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-fluid main-content" style="padding-bottom: 80px;">
|
<main id="site-main" class="main-content" style="padding: 0;">
|
||||||
<div class="container">
|
{{#sidebar_content}}
|
||||||
<div class="row">
|
{{#equal layout "sidebar-content"}}
|
||||||
<main class="col-12">
|
<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}}
|
{{>content_template}}
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</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>
|
||||||
|
{{/sidebar_content}}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer id="site-footer">
|
||||||
{{>footer}}
|
{{>footer}}
|
||||||
|
</footer>
|
||||||
|
|
||||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="/assets/js/app.js"></script>
|
<script src="/assets/js/app.js"></script>
|
||||||
|
|||||||
@ -1,191 +1,68 @@
|
|||||||
# CodePress CMS Guide
|
# CodePress CMS Guide
|
||||||
|
|
||||||
## Welcome to CodePress CMS
|
## Welcome to CodePress
|
||||||
|
|
||||||
CodePress is a lightweight, file-based Content Management System built with PHP and Bootstrap.
|
CodePress is a lightweight, file-based Content Management System built with PHP and Bootstrap.
|
||||||
|
|
||||||
### Table of Contents
|
## Features
|
||||||
|
|
||||||
1. [Getting Started](#getting-started)
|
### 🏠 Navigation
|
||||||
2. [Content Management](#content-management)
|
- Tab-style navigation with Bootstrap styling
|
||||||
3. [Templates](#templates)
|
- Dropdown menus for folders and sub-folders
|
||||||
4. [Configuration](#configuration)
|
- Home button with icon
|
||||||
|
- Automatic menu generation
|
||||||
|
- Responsive design
|
||||||
|
- Breadcrumb navigation
|
||||||
|
- Active state marking
|
||||||
|
|
||||||
---
|
### 📄 Content Types
|
||||||
|
- **Markdown (.md)** - CommonMark support
|
||||||
|
- **PHP (.php)** - Dynamic content
|
||||||
|
- **HTML (.html)** - Static HTML pages
|
||||||
|
- **Directory listings** - Automatic directory overviews
|
||||||
|
- **Language-specific content** - `en.` and `nl.` prefixes
|
||||||
|
|
||||||
## Getting Started
|
### 🔍 Search Functionality
|
||||||
|
- Full-text search through all content
|
||||||
|
- Results with snippets and highlighting
|
||||||
|
- Direct navigation to found pages
|
||||||
|
- SEO-friendly search results
|
||||||
|
- Search URL: `?search=query`
|
||||||
|
|
||||||
### Requirements
|
### 🧭 Configuration
|
||||||
- PHP 8.4+
|
- **JSON configuration** in `config.json`
|
||||||
- Web server (Apache/Nginx)
|
- Dynamic homepage setting
|
||||||
- Modern web browser
|
- SEO settings (description, keywords)
|
||||||
- Write permissions for content directory
|
- Author information with links
|
||||||
|
- Theme configuration with colors
|
||||||
|
- Language settings
|
||||||
|
- Feature toggles
|
||||||
|
|
||||||
### Installation
|
### 🎨 Layout & Design
|
||||||
1. Clone or download the CodePress files
|
- Flexbox layout for responsive structure
|
||||||
|
- Fixed header with logo and search
|
||||||
|
- Breadcrumb navigation
|
||||||
|
- Fixed footer with file info and links
|
||||||
|
- Bootstrap 5 styling
|
||||||
|
- Mustache templates
|
||||||
|
- Semantic HTML5 structure
|
||||||
|
- **Dynamic layouts** with YAML frontmatter
|
||||||
|
- **Sidebar support** with plugin integration
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone or download CodePress files
|
||||||
2. Upload to your web server
|
2. Upload to your web server
|
||||||
3. Make sure the `content/` directory is writable
|
3. Make sure `content/` directory is writable
|
||||||
4. Navigate to your website in the browser
|
4. Navigate to your website in browser
|
||||||
|
|
||||||
### Basic Configuration
|
## Configuration
|
||||||
The most important settings are in `engine/core/config.php`:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$config = [
|
|
||||||
'site_title' => 'My Website',
|
|
||||||
'default_page' => 'home',
|
|
||||||
'content_dir' => __DIR__ . '/../../content',
|
|
||||||
'templates_dir' => __DIR__ . '/../templates'
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Content Management {#content-management}
|
|
||||||
|
|
||||||
### File Structure
|
|
||||||
```
|
|
||||||
content/
|
|
||||||
├── home.md # Home page
|
|
||||||
├── blog/
|
|
||||||
│ ├── index.md # Blog overview
|
|
||||||
│ ├── article-1.md # Blog article
|
|
||||||
│ └── category/
|
|
||||||
│ └── article.md # Article in category
|
|
||||||
└── about-us/
|
|
||||||
└── info.md # About us page
|
|
||||||
```
|
|
||||||
|
|
||||||
### Content Types
|
|
||||||
CodePress supports four content types:
|
|
||||||
|
|
||||||
#### Markdown (`.md`)
|
|
||||||
```markdown
|
|
||||||
# Page Title
|
|
||||||
|
|
||||||
This is page content in **Markdown** format with CommonMark extensions.
|
|
||||||
|
|
||||||
## Subsection
|
|
||||||
|
|
||||||
- [x] Task list item
|
|
||||||
- [ ] Another task
|
|
||||||
- **Bold** and *italic* text
|
|
||||||
- [Auto-linked pages](?page=another-page)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### PHP (`.php`)
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
$title = "Dynamic Page";
|
|
||||||
?>
|
|
||||||
<h1><?php echo htmlspecialchars($title); ?></h1>
|
|
||||||
<p>This is dynamic content with PHP.</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### HTML (`.html`)
|
|
||||||
```html
|
|
||||||
<h1>HTML Page</h1>
|
|
||||||
<p>This is static HTML content.</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Directory Listings
|
|
||||||
Directories automatically generate listings:
|
|
||||||
- File information (size, dates)
|
|
||||||
- Navigation to subdirectories
|
|
||||||
- Responsive file listing layout
|
|
||||||
|
|
||||||
#### PHP (`.php`)
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
$title = "Dynamic Page";
|
|
||||||
?>
|
|
||||||
<h1><?php echo $title; ?></h1>
|
|
||||||
<p>This is dynamic content with PHP.</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### HTML (`.html`)
|
|
||||||
```html
|
|
||||||
<h1>HTML Page</h1>
|
|
||||||
<p>This is static HTML content.</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Automatic Linking
|
|
||||||
CodePress automatically creates links to other pages when you mention page names in your content.
|
|
||||||
|
|
||||||
### Language Support
|
|
||||||
- **Browser detection**: Automatic language detection
|
|
||||||
- **URL switching**: `?lang=en` or `?lang=nl`
|
|
||||||
- **Language prefixes**: `en.page.md` and `nl.page.md`
|
|
||||||
- **Translation files**: `lang/en.php` and `lang/nl.php`
|
|
||||||
|
|
||||||
### Search Functionality
|
|
||||||
- **Full-text search**: Search through all content
|
|
||||||
- **Search URL**: `?search=query` for bookmarkable searches
|
|
||||||
- **Result highlighting**: Search terms highlighted in results
|
|
||||||
- **File and content search**: Searches both filenames and content
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Templates {#templates}
|
|
||||||
|
|
||||||
### Template Structure
|
|
||||||
CodePress uses Mustache-compatible templates:
|
|
||||||
|
|
||||||
- `layout.mustache` - Main template
|
|
||||||
- `assets/header.mustache` - Header component
|
|
||||||
- `assets/sidebar.mustache` - Sidebar navigation
|
|
||||||
- `assets/footer.mustache` - Footer component
|
|
||||||
|
|
||||||
### Template Variables
|
|
||||||
Available variables in templates:
|
|
||||||
|
|
||||||
#### **Site Info**
|
|
||||||
- `{{site_title}}` - Website title
|
|
||||||
- `{{author_name}}` - Author name
|
|
||||||
- `{{author_website}}` - Author website
|
|
||||||
- `{{author_git}}` - Git repository link
|
|
||||||
|
|
||||||
#### **Page Info**
|
|
||||||
- `{{page_title}}` - Page title (filename without extension)
|
|
||||||
- `{{content}}` - Page content (HTML)
|
|
||||||
- `{{file_info}}` - File information (dates, size)
|
|
||||||
- `{{is_homepage}}` - Boolean: is this the homepage?
|
|
||||||
|
|
||||||
#### **Navigation**
|
|
||||||
- `{{menu}}` - Navigation menu
|
|
||||||
- `{{breadcrumb}}` - Breadcrumb navigation
|
|
||||||
- `{{homepage}}` - Homepage link
|
|
||||||
- `{{homepage_title}}` - Homepage title
|
|
||||||
|
|
||||||
#### **Theme**
|
|
||||||
- `{{header_color}}` - Header background color
|
|
||||||
- `{{header_font_color}}` - Header text color
|
|
||||||
- `{{navigation_color}}` - Navigation background color
|
|
||||||
- `{{navigation_font_color}}` - Navigation text color
|
|
||||||
|
|
||||||
#### **Language**
|
|
||||||
- `{{current_lang}}` - Current language (en/nl)
|
|
||||||
- `{{current_lang_upper}}` - Current language (EN/NL)
|
|
||||||
- `{{available_langs}}` - Available languages
|
|
||||||
- `{{t_*}}` - Translated strings (t_home, t_search, etc.)
|
|
||||||
|
|
||||||
#### **SEO**
|
|
||||||
- `{{seo_description}}` - Meta description
|
|
||||||
- `{{seo_keywords}}` - Meta keywords
|
|
||||||
|
|
||||||
#### **Features**
|
|
||||||
- `{{has_content}}` - Boolean: is content available?
|
|
||||||
- `{{show_site_link}}` - Boolean: show site link?
|
|
||||||
- `{{is_guide_page}}` - Boolean: is this the guide page?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Configuration {#configuration}
|
|
||||||
|
|
||||||
### Basic Settings
|
### Basic Settings
|
||||||
|
|
||||||
Edit `config.json` in your project root:
|
Edit `config.json` in your project root:
|
||||||
|
|
||||||
```json
|
\`\`\`json
|
||||||
{
|
{
|
||||||
"site_title": "Your Website Name",
|
"site_title": "Your Website Name",
|
||||||
"content_dir": "content",
|
"content_dir": "content",
|
||||||
@ -199,12 +76,13 @@ Edit `config.json` in your project root:
|
|||||||
"header_color": "#0a369d",
|
"header_color": "#0a369d",
|
||||||
"header_font_color": "#ffffff",
|
"header_font_color": "#ffffff",
|
||||||
"navigation_color": "#2754b4",
|
"navigation_color": "#2754b4",
|
||||||
"navigation_font_color": "#ffffff"
|
"navigation_font_color": "#ffffff",
|
||||||
|
"sidebar_background": "#f8f9fa",
|
||||||
|
"sidebar_border": "#dee2e6"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Your Name",
|
"name": "Your Name",
|
||||||
"website": "https://yourwebsite.com",
|
"website": "https://yourwebsite.com"
|
||||||
"git": "https://github.com/youruser/codepress"
|
|
||||||
},
|
},
|
||||||
"seo": {
|
"seo": {
|
||||||
"description": "Your website description",
|
"description": "Your website description",
|
||||||
@ -216,70 +94,232 @@ Edit `config.json` in your project root:
|
|||||||
"breadcrumbs_enabled": true
|
"breadcrumbs_enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
### SEO Friendly URLs
|
## Content Management
|
||||||
CodePress automatically generates clean URLs:
|
|
||||||
- `home.md` → `?page=home`
|
|
||||||
- `blog/article.md` → `?page=blog/article`
|
|
||||||
- `nl.page.md` → `?page=nl.page&lang=nl`
|
|
||||||
|
|
||||||
### Language Support
|
### File Structure
|
||||||
- **Browser detection**: Automatic language detection
|
|
||||||
- **URL switching**: `?lang=en` or `?lang=nl`
|
\`\`\`
|
||||||
|
content/
|
||||||
|
├── home.md # Home page
|
||||||
|
├── blog/
|
||||||
|
│ ├── index.md # Blog overview
|
||||||
|
│ ├── article-1.md # Blog article
|
||||||
|
│ └── category/
|
||||||
|
│ └── article.md # Article in category
|
||||||
|
└── about-us/
|
||||||
|
└── info.md # About us page
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Content Types
|
||||||
|
|
||||||
|
#### Markdown (`.md`)
|
||||||
|
\`\`\`markdown
|
||||||
|
# Page Title
|
||||||
|
|
||||||
|
This is page content in **Markdown** format with CommonMark extensions.
|
||||||
|
|
||||||
|
## Subsection
|
||||||
|
|
||||||
|
- [x] Task list item
|
||||||
|
- [ ] Another task
|
||||||
|
- **Bold** and *italic* text
|
||||||
|
- [Auto-linked pages](?page=another-page)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
#### PHP (`.php`)
|
||||||
|
\`\`\`php
|
||||||
|
<?php
|
||||||
|
$title = "Dynamic Page";
|
||||||
|
?>
|
||||||
|
<h1><?php echo htmlspecialchars($title); ?></h1>
|
||||||
|
<p>This is dynamic content with PHP.</p>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
#### HTML (`.html`)
|
||||||
|
\`\`\`html
|
||||||
|
<h1>HTML Page</h1>
|
||||||
|
<p>This is static HTML content.</p>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### File Naming Conventions
|
||||||
|
|
||||||
|
- **Lowercase names**: Use lowercase for all files
|
||||||
|
- **No spaces**: Use hyphens (-) or underscores (_)
|
||||||
- **Language prefixes**: `en.page.md` and `nl.page.md`
|
- **Language prefixes**: `en.page.md` and `nl.page.md`
|
||||||
- **Directory precedence**: Directories take precedence over files
|
- **Display names**: `file-name.md` displays as "File Name" in menus
|
||||||
|
|
||||||
### Search Functionality
|
## Templates
|
||||||
The built-in search function searches through:
|
|
||||||
- File names
|
|
||||||
- Content of Markdown/PHP/HTML files
|
|
||||||
- Search URL: `?search=query` for bookmarkable searches
|
|
||||||
- Result highlighting and snippets
|
|
||||||
|
|
||||||
### Directory Listings
|
### Template Variables
|
||||||
- **Auto-generation**: `?page=directory` shows directory contents
|
|
||||||
- **File information**: Creation/modification dates and sizes
|
|
||||||
- **Navigation**: Links to files and subdirectories
|
|
||||||
|
|
||||||
|
#### Site Info
|
||||||
|
- `site_title` - Website title
|
||||||
|
- `author_name` - Author name
|
||||||
|
- `author_website` - Author website
|
||||||
|
- `author_git` - Git repository link
|
||||||
|
|
||||||
|
#### Page Info
|
||||||
|
- `page_title` - Page title (filename without extension)
|
||||||
|
- `content` - Page content (HTML)
|
||||||
|
- `file_info` - File information (dates, size)
|
||||||
|
- `is_homepage` - Boolean: is this homepage?
|
||||||
|
|
||||||
|
#### Navigation
|
||||||
|
- `menu` - Navigation menu
|
||||||
|
- `breadcrumb` - Breadcrumb navigation
|
||||||
|
- `homepage` - Homepage link
|
||||||
|
|
||||||
|
#### Theme
|
||||||
|
- `header_color` - Header background color
|
||||||
|
- `header_font_color` - Header text color
|
||||||
|
- `navigation_color` - Navigation background color
|
||||||
|
- `navigation_font_color` - Navigation text color
|
||||||
|
|
||||||
|
#### Language
|
||||||
|
- `current_lang` - Current language (en/nl)
|
||||||
|
- `current_lang_upper` - Current language (EN/NL)
|
||||||
|
- `t_*` - Translated strings
|
||||||
|
|
||||||
|
## URL Structure
|
||||||
|
|
||||||
|
### Basic URLs
|
||||||
|
- **Home**: `/` or `?page=home`
|
||||||
|
- **Page**: `?page=blog/article`
|
||||||
|
- **Search**: `?search=query`
|
||||||
|
- **Guide**: `?guide`
|
||||||
|
- **Language**: `?lang=en` or `?lang=nl`
|
||||||
|
|
||||||
|
## SEO Optimization
|
||||||
|
|
||||||
|
### Meta Tags
|
||||||
|
|
||||||
|
The CMS automatically adds meta tags:
|
||||||
|
|
||||||
|
\`\`\`html
|
||||||
|
<meta name="generator" content="CodePress CMS">
|
||||||
|
<meta name="application-name" content="CodePress">
|
||||||
|
<meta name="author" content="Your Name">
|
||||||
|
<meta name="description" content="...">
|
||||||
|
<meta name="keywords" content="...">
|
||||||
|
<link rel="author" href="https://yourwebsite.com">
|
||||||
|
<link rel="me" href="https://github.com/youruser/codepress">
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 🔌 Plugin System
|
||||||
|
|
||||||
|
### Plugin Structure
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
plugins/
|
||||||
|
├── README.md # Plugin documentation
|
||||||
|
├── HTMLBlock/
|
||||||
|
│ ├── HTMLBlock.php # Plugin class
|
||||||
|
│ └── README.md # Plugin specific documentation
|
||||||
|
└── MQTTTracker/
|
||||||
|
├── MQTTTracker.php # Plugin class
|
||||||
|
├── config.json # Plugin configuration
|
||||||
|
└── README.md # Plugin documentation
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Plugin Development
|
||||||
|
|
||||||
|
- **API access** via `CMSAPI` class
|
||||||
|
- **Sidebar content** with `getSidebarContent()`
|
||||||
|
- **Metadata access** from YAML frontmatter
|
||||||
|
- **Configuration** via JSON files
|
||||||
|
- **Event hooks** for extension
|
||||||
|
|
||||||
|
### Available Plugins
|
||||||
|
|
||||||
|
- **HTMLBlock** - Custom HTML blocks in sidebar
|
||||||
|
- **MQTTTracker** - Real-time analytics and tracking
|
||||||
|
|
||||||
|
## 🎯 Template System
|
||||||
|
|
||||||
|
### Layout Options
|
||||||
|
|
||||||
|
Use YAML frontmatter to select layout:
|
||||||
|
|
||||||
|
\`\`\`yaml
|
||||||
---
|
---
|
||||||
|
title: My Page
|
||||||
|
layout: sidebar-content
|
||||||
|
---
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Available Layouts
|
||||||
|
|
||||||
|
- `sidebar-content` - Sidebar left, content right (default)
|
||||||
|
- `content` - Content only (full width)
|
||||||
|
- `sidebar` - Sidebar only
|
||||||
|
- `content-sidebar` - Content left, sidebar right
|
||||||
|
- `content-sidebar-reverse` - Content right, sidebar left
|
||||||
|
|
||||||
|
### Meta Data
|
||||||
|
|
||||||
|
\`\`\`yaml
|
||||||
|
---
|
||||||
|
title: Page Title
|
||||||
|
layout: content-sidebar
|
||||||
|
description: Page description
|
||||||
|
author: Author Name
|
||||||
|
date: 2025-11-26
|
||||||
|
---
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 📊 Analytics & Tracking
|
||||||
|
|
||||||
|
### MQTT Tracker
|
||||||
|
|
||||||
|
- Real-time page tracking
|
||||||
|
- Session management
|
||||||
|
- Business Intelligence data
|
||||||
|
- Privacy aware (GDPR compliant)
|
||||||
|
- MQTT integration for dashboards
|
||||||
|
|
||||||
|
### Data Format
|
||||||
|
|
||||||
|
\`\`\`json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-11-26T15:30:00+00:00",
|
||||||
|
"session_id": "cms_1234567890abcdef",
|
||||||
|
"page_url": "?page=demo/sidebar-content&lang=en",
|
||||||
|
"page_title": "Sidebar-Content Layout",
|
||||||
|
"language": "en",
|
||||||
|
"layout": "sidebar-content"
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
## Tips and Tricks
|
## Tips and Tricks
|
||||||
|
|
||||||
### Page Organization
|
### Page Organization
|
||||||
|
|
||||||
- Use subdirectories for categories
|
- Use subdirectories for categories
|
||||||
- Give each directory an `index.md` for an overview page
|
- Give each directory an `index.md` for an overview page
|
||||||
- Keep file names short and descriptive
|
- Keep file names short and descriptive
|
||||||
- Use language prefixes: `en.page.md` and `nl.page.md`
|
- Use language prefixes: `en.page.md` and `nl.page.md`
|
||||||
- Directory names take precedence over files with same name
|
|
||||||
|
|
||||||
### File Naming Conventions
|
|
||||||
- **Lowercase names**: Use lowercase for all files
|
|
||||||
- **No spaces**: Use hyphens (-) or underscores (_)
|
|
||||||
- **Language prefixes**: `en.` or `nl.` for multilingual content
|
|
||||||
- **Display names**: `file-name.md` displays as "File Name" in menus
|
|
||||||
- **Special cases**: `phpinfo` → "phpinfo", `ict` → "ICT"
|
|
||||||
|
|
||||||
### Content Optimization
|
### Content Optimization
|
||||||
|
|
||||||
- Use clear headings (H1, H2, H3)
|
- Use clear headings (H1, H2, H3)
|
||||||
- Add descriptive meta information
|
- Add descriptive meta information
|
||||||
- Use internal links for better navigation
|
- Use internal links for better navigation
|
||||||
|
|
||||||
### Security
|
## Troubleshooting
|
||||||
- Keep your CodePress installation updated
|
|
||||||
- Restrict write permissions on the `content/` directory
|
|
||||||
- Use HTTPS when possible
|
|
||||||
|
|
||||||
---
|
### Common Issues
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
### Troubleshooting
|
|
||||||
- **Empty pages**: Check file permissions
|
- **Empty pages**: Check file permissions
|
||||||
- **Template errors**: Verify template syntax
|
- **Template errors**: Verify template syntax
|
||||||
- **404 errors**: Check file names and paths
|
- **404 errors**: Check file names and paths
|
||||||
|
- **Navigation not updated**: Reload the page
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
### More Information
|
### More Information
|
||||||
|
|
||||||
- Documentation: [CodePress GitHub](https://git.noorlander.info/E.Noorlander/CodePress.git)
|
- Documentation: [CodePress GitHub](https://git.noorlander.info/E.Noorlander/CodePress.git)
|
||||||
- Issues and feature requests: GitHub Issues
|
- Issues and feature requests: GitHub Issues
|
||||||
|
|
||||||
|
|||||||
@ -2,84 +2,70 @@
|
|||||||
|
|
||||||
## Overzicht
|
## Overzicht
|
||||||
|
|
||||||
CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd met PHP. Het is ontworpen om eenvoudig te gebruiken, flexibel te zijn en zonder database te werken.
|
CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd met PHP. Werkt zonder database.
|
||||||
|
|
||||||
## Functies
|
## Functies
|
||||||
|
|
||||||
### 🏠 **Navigatie**
|
### 🏠 Navigatie
|
||||||
- **Tab-style navigatie** met Bootstrap styling
|
- Tab-style navigatie met Bootstrap styling
|
||||||
- **Dropdown menus** voor mappen en sub-mappen
|
- Dropdown menus voor mappen en sub-mappen
|
||||||
- **Home knop** met icoon die linkt naar de ingestelde homepage
|
- Home knop met icoon
|
||||||
- **Automatische menu generatie** op basis van content structuur
|
- Automatische menu generatie
|
||||||
- **Responsive design** voor mobiele apparaten
|
- Responsive design
|
||||||
- **Breadcrumb navigatie** met home icoon en pad weergave
|
- Breadcrumb navigatie
|
||||||
- **Active state marking** voor huidige pagina in menu
|
- Active state marking
|
||||||
|
|
||||||
### 📄 **Content Types**
|
### 📄 Content Types
|
||||||
- **Markdown (.md)** - Met CommonMark ondersteuning en extensies (autolink, strikethrough, tables, task lists)
|
- **Markdown (.md)** - CommonMark ondersteuning
|
||||||
- **PHP (.php)** - Voor dynamische content en functionaliteit
|
- **PHP (.php)** - Dynamische content
|
||||||
- **HTML (.html)** - Voor statische HTML pagina's
|
- **HTML (.html)** - Statische HTML pagina's
|
||||||
- **Directory listings** - Automatische generatie van directory overzichten
|
- **Directory listings** - Automatische directory overzichten
|
||||||
- **Language-specific content** - `nl.` en `en.` prefix voor meertalige content
|
- **Language-specific content** - `nl.` en `en.` prefix
|
||||||
- **Automatische template selectie** op basis van bestandstype
|
|
||||||
|
|
||||||
### 🔍 **Zoekfunctionaliteit**
|
### 🔍 Zoekfunctionaliteit
|
||||||
- **Volledige tekst zoek** door alle content bestanden
|
- Volledige tekst zoek door alle content
|
||||||
- **Resultaten met snippets** en highlighting
|
- Resultaten met snippets en highlighting
|
||||||
- **Directe navigatie** naar gevonden pagina's
|
- Directe navigatie naar gevonden pagina's
|
||||||
- **SEO-vriendelijke** zoekresultaten
|
- SEO-vriendelijke zoekresultaten
|
||||||
- **Search URL**: `?search=zoekterm` voor bookmarkable searches
|
- Search URL: `?search=zoekterm`
|
||||||
|
|
||||||
### 🧭 **Configuratie**
|
### 🧭 Configuratie
|
||||||
- **JSON configuratie** in project root (`config.json`)
|
- **JSON configuratie** in `config.json`
|
||||||
- **Dynamische homepage** instelling of automatische detectie
|
- Dynamische homepage instelling
|
||||||
- **SEO instellingen** (description, keywords)
|
- SEO instellingen (description, keywords)
|
||||||
- **Author informatie** met links naar website en Git
|
- Author informatie met links
|
||||||
- **Thema configuratie** met kleur instellingen
|
- Thema configuratie met kleuren
|
||||||
- **Language settings** voor meertalige content
|
- Language settings
|
||||||
- **Feature toggles** voor search, breadcrumbs, auto-linking
|
- Feature toggles
|
||||||
|
|
||||||
### 🎨 **Layout & Design**
|
### 🎨 Layout & Design
|
||||||
- **Flexbox layout** voor moderne, responsive structuur
|
- Flexbox layout voor responsive structuur
|
||||||
- **Fixed header** met logo en zoekfunctie
|
- Fixed header met logo en zoekfunctie
|
||||||
- **Breadcrumb navigatie** tussen header en content
|
- Breadcrumb navigatie
|
||||||
- **Fixed footer** met file info en links
|
- Fixed footer met file info en links
|
||||||
- **Bootstrap 5** styling en componenten
|
- Bootstrap 5 styling
|
||||||
- **Custom CSS** voor specifieke styling
|
- Mustache templates
|
||||||
- **Mustache templates** met conditionals en partials
|
- Semantic HTML5 structuur
|
||||||
- **Semantic HTML5** structuur voor SEO
|
- **Dynamic layouts** met YAML frontmatter
|
||||||
|
- **Sidebar support** met plugin integratie
|
||||||
### 📱 **Responsive Features**
|
|
||||||
- **Mobile-first** aanpak
|
|
||||||
- **Hamburger menu** voor kleine schermen
|
|
||||||
- **Touch-friendly** dropdowns en navigatie
|
|
||||||
- **Adaptieve breedtes** voor verschillende schermgroottes
|
|
||||||
|
|
||||||
### 🌍 **Language Support**
|
|
||||||
- **Browser language detection** op basis van Accept-Language header
|
|
||||||
- **URL language switching**: `?lang=nl` of `?lang=en`
|
|
||||||
- **Translation files**: `lang/nl.php` en `lang/en.php`
|
|
||||||
- **Language prefixes**: `nl.bestand.md` en `en.bestand.md`
|
|
||||||
- **Automatic language routing** voor meertalige content
|
|
||||||
|
|
||||||
## Installatie
|
## Installatie
|
||||||
|
|
||||||
1. **Upload bestanden** naar webserver
|
1. Upload bestanden naar webserver
|
||||||
2. **Stel permissies in** voor webserver
|
2. Stel permissies in voor webserver
|
||||||
3. **Configureer** `config.json` indien nodig
|
3. Configureer `config.json` indien nodig
|
||||||
4. **Toegang** tot website via browser
|
4. Toegang tot website via browser
|
||||||
|
|
||||||
## Configuratie
|
## Configuratie
|
||||||
|
|
||||||
### Basis Configuratie (`config.json`)
|
### Basis Configuratie (`config.json`)
|
||||||
|
|
||||||
```json
|
\`\`\`json
|
||||||
{
|
{
|
||||||
"site_title": "CodePress",
|
"site_title": "CodePress",
|
||||||
"content_dir": "content",
|
"content_dir": "content",
|
||||||
"templates_dir": "engine/templates",
|
"templates_dir": "engine/templates",
|
||||||
"default_page": "auto",
|
"default_page": "auto",
|
||||||
"homepage": "welkom",
|
|
||||||
"language": {
|
"language": {
|
||||||
"default": "nl",
|
"default": "nl",
|
||||||
"available": ["nl", "en"]
|
"available": ["nl", "en"]
|
||||||
@ -88,12 +74,13 @@ CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd
|
|||||||
"header_color": "#0a369d",
|
"header_color": "#0a369d",
|
||||||
"header_font_color": "#ffffff",
|
"header_font_color": "#ffffff",
|
||||||
"navigation_color": "#2754b4",
|
"navigation_color": "#2754b4",
|
||||||
"navigation_font_color": "#ffffff"
|
"navigation_font_color": "#ffffff",
|
||||||
|
"sidebar_background": "#f8f9fa",
|
||||||
|
"sidebar_border": "#dee2e6"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Edwin Noorlander",
|
"name": "E. Noorlander",
|
||||||
"website": "https://noorlander.info",
|
"website": "https://noorlander.info"
|
||||||
"git": "https://git.noorlander.info/E.Noorlander/CodePress.git"
|
|
||||||
},
|
},
|
||||||
"seo": {
|
"seo": {
|
||||||
"description": "CodePress CMS - Lightweight file-based content management system",
|
"description": "CodePress CMS - Lightweight file-based content management system",
|
||||||
@ -105,27 +92,13 @@ CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd
|
|||||||
"breadcrumbs_enabled": true
|
"breadcrumbs_enabled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
### Configuratie Opties
|
|
||||||
|
|
||||||
- **`site_title`** - Naam van de website
|
|
||||||
- **`content_dir`** - Map met content bestanden
|
|
||||||
- **`templates_dir`** - Map met template bestanden
|
|
||||||
- **`default_page`** - Standaard pagina (`"auto"` voor automatische detectie)
|
|
||||||
- **`homepage`** - Specifieke homepage (`"auto"` voor automatische detectie)
|
|
||||||
- **`language.default`** - Standaard taal
|
|
||||||
- **`language.available`** - Beschikbare talen
|
|
||||||
- **`theme.*`** - Kleur instellingen voor header en navigatie
|
|
||||||
- **`author`** - Auteur informatie met links
|
|
||||||
- **`seo`** - SEO instellingen
|
|
||||||
- **`features.*`** - Feature toggles (search, breadcrumbs, auto-linking)
|
|
||||||
|
|
||||||
## Content Structuur
|
## Content Structuur
|
||||||
|
|
||||||
### Bestandsstructuur
|
### Bestandsstructuur
|
||||||
|
|
||||||
```
|
\`\`\`
|
||||||
content/
|
content/
|
||||||
├── map1/
|
├── map1/
|
||||||
│ ├── submap1/
|
│ ├── submap1/
|
||||||
@ -136,74 +109,47 @@ content/
|
|||||||
│ └── pagina4.md
|
│ └── pagina4.md
|
||||||
├── homepage.md
|
├── homepage.md
|
||||||
└── index.html
|
└── index.html
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
### Bestandsnamen
|
### Bestandsnamen
|
||||||
|
|
||||||
- **Gebruik lowercase** bestandsnamen
|
- Gebruik lowercase bestandsnamen
|
||||||
- **Geen spaties** - gebruik `-` of `_` als scheidingsteken
|
- Geen spaties - gebruik `-` of `_`
|
||||||
- **Logische extensies** - `.md`, `.php`, `.html`
|
- Logische extensies - `.md`, `.php`, `.html`
|
||||||
- **Unieke namen** - geen duplicaten binnen dezelfde map
|
- Unieke namen - geen duplicaten
|
||||||
- **Language prefixes** - `nl.bestand.md` en `en.bestand.md` voor meertalige content
|
- Language prefixes - `nl.bestand.md` en `en.bestand.md`
|
||||||
- **Display names** - `bestands-naam` wordt automatisch "Bestands Naam" in menu's
|
|
||||||
- **Special cases** - `phpinfo` → "phpinfo", `ict` → "ICT"
|
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
### Template Structuur
|
|
||||||
|
|
||||||
```
|
|
||||||
engine/templates/
|
|
||||||
├── layout.mustache - Hoofd layout template
|
|
||||||
├── assets/
|
|
||||||
│ ├── header.mustache - Header template
|
|
||||||
│ ├── navigation.mustache - Navigatie template
|
|
||||||
│ └── footer.mustache - Footer template
|
|
||||||
├── markdown_content.mustache - Markdown content template
|
|
||||||
├── php_content.mustache - PHP content template
|
|
||||||
└── html_content.mustache - HTML content template
|
|
||||||
```
|
|
||||||
|
|
||||||
### Template Variabelen
|
### Template Variabelen
|
||||||
|
|
||||||
#### **Site Info**
|
#### Site Info
|
||||||
- **`{{site_title}}`** - Website titel
|
- `site_title` - Website titel
|
||||||
- **`{{author_name}}`** - Auteur naam
|
- `author_name` - Auteur naam
|
||||||
- **`{{author_website}}`** - Auteur website
|
- `author_website` - Auteur website
|
||||||
- **`{{author_git}}`** - Git repository link
|
- `author_git` - Git repository link
|
||||||
|
|
||||||
#### **Page Info**
|
#### Page Info
|
||||||
- **`{{page_title}}`** - Pagina titel (bestandsnaam zonder extensie)
|
- `page_title` - Pagina titel
|
||||||
- **`{{content}}`** - Content (HTML)
|
- `content` - Content (HTML)
|
||||||
- **`{{file_info}}`** - Bestandsinformatie (datum, grootte)
|
- `file_info` - Bestandsinformatie
|
||||||
- **`{{is_homepage}}`** - Boolean: is dit de homepage?
|
- `is_homepage` - Boolean: is dit de homepage?
|
||||||
|
|
||||||
#### **Navigation**
|
#### Navigation
|
||||||
- **`{{menu}}`** - Navigatie menu
|
- `menu` - Navigatie menu
|
||||||
- **`{{breadcrumb}}`** - Breadcrumb navigatie
|
- `breadcrumb` - Breadcrumb navigatie
|
||||||
- **`{{homepage}}`** - Homepage link
|
- `homepage` - Homepage link
|
||||||
- **`{{homepage_title}}`** - Homepage titel
|
|
||||||
|
|
||||||
#### **Theme**
|
#### Theme
|
||||||
- **`{{header_color}}`** - Header achtergrondkleur
|
- `header_color` - Header achtergrondkleur
|
||||||
- **`{{header_font_color}}`** - Header tekstkleur
|
- `header_font_color` - Header tekstkleur
|
||||||
- **`{{navigation_color}}`** - Navigatie achtergrondkleur
|
- `navigation_color` - Navigatie achtergrondkleur
|
||||||
- **`{{navigation_font_color}}`** - Navigatie tekstkleur
|
- `navigation_font_color` - Navigatie tekstkleur
|
||||||
|
|
||||||
#### **Language**
|
#### Language
|
||||||
- **`{{current_lang}}`** - Huidige taal (nl/en)
|
- `current_lang` - Huidige taal (nl/en)
|
||||||
- **`{{current_lang_upper}}`** - Huidige taal (NL/EN)
|
- `current_lang_upper` - Huidige taal (NL/EN)
|
||||||
- **`{{available_langs}}`** - Beschikbare talen
|
- `t_*` - Vertaalde strings
|
||||||
- **`{{t_*}}`** - Vertaalde strings (t_home, t_search, etc.)
|
|
||||||
|
|
||||||
#### **SEO**
|
|
||||||
- **`{{seo_description}}`** - Meta description
|
|
||||||
- **`{{seo_keywords}}`** - Meta keywords
|
|
||||||
|
|
||||||
#### **Features**
|
|
||||||
- **`{{has_content}}`** - Boolean: is er content beschikbaar?
|
|
||||||
- **`{{show_site_link}}`** - Boolean: toon site link?
|
|
||||||
- **`{{is_guide_page}}`** - Boolean: is dit de handleiding pagina?
|
|
||||||
|
|
||||||
## URL Structuur
|
## URL Structuur
|
||||||
|
|
||||||
@ -214,99 +160,121 @@ engine/templates/
|
|||||||
- **Handleiding**: `?guide`
|
- **Handleiding**: `?guide`
|
||||||
- **Language**: `?lang=nl` of `?lang=en`
|
- **Language**: `?lang=nl` of `?lang=en`
|
||||||
|
|
||||||
### File Extensions
|
|
||||||
Bestandsextensies worden automatisch gedetecteerd:
|
|
||||||
- `pagina.md` → `?page=pagina`
|
|
||||||
- `pagina.php` → `?page=pagina`
|
|
||||||
- `pagina.html` → `?page=pagina`
|
|
||||||
|
|
||||||
### Language Support
|
|
||||||
- **Browser detection**: Automatische taal detectie
|
|
||||||
- **URL switching**: `?page=nl.bestand&lang=nl`
|
|
||||||
- **Language prefixes**: `nl.bestand.md` en `en.bestand.md`
|
|
||||||
- **Directory precedence**: Directories hebben voorrang op bestanden
|
|
||||||
|
|
||||||
### Directory Listings
|
|
||||||
- **Auto-generation**: `?page=map` toont directory inhoud
|
|
||||||
- **File info**: Creatie/modificatie datums en groottes
|
|
||||||
- **Navigation**: Links naar bestanden en submappen
|
|
||||||
|
|
||||||
## SEO Optimalisatie
|
## SEO Optimalisatie
|
||||||
|
|
||||||
### Meta Tags
|
### Meta Tags
|
||||||
|
|
||||||
De CMS voegt automatisch de volgende meta tags toe:
|
De CMS voegt automatisch meta tags toe:
|
||||||
|
\`\`\`html
|
||||||
```html
|
|
||||||
<meta name="generator" content="CodePress CMS">
|
<meta name="generator" content="CodePress CMS">
|
||||||
<meta name="application-name" content="CodePress">
|
<meta name="author" content="E. Noorlander">
|
||||||
<meta name="author" content="Edwin Noorlander">
|
|
||||||
<meta name="description" content="...">
|
<meta name="description" content="...">
|
||||||
<meta name="keywords" content="...">
|
<meta name="keywords" content="...">
|
||||||
<link rel="author" href="https://noorlander.info">
|
\`\`\`
|
||||||
<link rel="me" href="https://git.noorlander.info/E.Noorlander/CodePress.git">
|
|
||||||
```
|
|
||||||
|
|
||||||
### Auto-linking
|
## 🔌 Plugin Systeem
|
||||||
|
|
||||||
De CMS linkt automatisch pagina titels naar hun content:
|
### Plugin Structuur
|
||||||
|
|
||||||
- **Automatische detectie** van pagina titels in tekst
|
\`\`\`
|
||||||
- **Slimme links** met `title` attributen
|
plugins/
|
||||||
- **Geen dubbele links** voor dezelfde pagina
|
├── README.md
|
||||||
- **SEO-vriendelijke** URL structuur
|
├── HTMLBlock/
|
||||||
|
│ ├── HTMLBlock.php
|
||||||
|
│ └── README.md
|
||||||
|
└── MQTTTracker/
|
||||||
|
├── MQTTTracker.php
|
||||||
|
├── config.json
|
||||||
|
└── README.md
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Plugin Development
|
||||||
|
|
||||||
|
- **API toegang** via `CMSAPI` class
|
||||||
|
- **Sidebar content** met `getSidebarContent()`
|
||||||
|
- **Metadata toegang** uit YAML frontmatter
|
||||||
|
- **Configuratie** via JSON bestanden
|
||||||
|
|
||||||
|
### Beschikbare Plugins
|
||||||
|
|
||||||
|
- **HTMLBlock** - Custom HTML blokken in sidebar
|
||||||
|
- **MQTTTracker** - Real-time analytics en tracking
|
||||||
|
|
||||||
|
## 🎯 Template Systeem
|
||||||
|
|
||||||
|
### Layout Opties
|
||||||
|
|
||||||
|
Gebruik YAML frontmatter om layout te selecteren:
|
||||||
|
|
||||||
|
\`\`\`yaml
|
||||||
|
---
|
||||||
|
title: Mijn Pagina
|
||||||
|
layout: sidebar-content
|
||||||
|
---
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Beschikbare Layouts
|
||||||
|
|
||||||
|
- `sidebar-content` - Sidebar links, content rechts (standaard)
|
||||||
|
- `content` - Alleen content (volle breedte)
|
||||||
|
- `sidebar` - Alleen sidebar
|
||||||
|
- `content-sidebar` - Content links, sidebar rechts
|
||||||
|
- `content-sidebar-reverse` - Content rechts, sidebar links
|
||||||
|
|
||||||
|
### Meta Data
|
||||||
|
|
||||||
|
\`\`\`yaml
|
||||||
|
---
|
||||||
|
title: Pagina Titel
|
||||||
|
layout: content-sidebar
|
||||||
|
description: Pagina beschrijving
|
||||||
|
author: Auteur Naam
|
||||||
|
date: 2025-11-26
|
||||||
|
---
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## 📊 Analytics & Tracking
|
||||||
|
|
||||||
|
### MQTT Tracker
|
||||||
|
|
||||||
|
- Real-time page tracking
|
||||||
|
- Session management
|
||||||
|
- Business Intelligence data
|
||||||
|
- Privacy aware (GDPR compliant)
|
||||||
|
- MQTT integration voor dashboards
|
||||||
|
|
||||||
## Veelgestelde Vragen
|
## Veelgestelde Vragen
|
||||||
|
|
||||||
### Hoe stel ik de homepage in?
|
### Hoe stel ik de homepage in?
|
||||||
|
|
||||||
1. **Automatisch**: Laat de CMS het eerste bestand kiezen
|
1. **Automatisch**: Laat de CMS het eerste bestand kiezen
|
||||||
2. **Handmatig**: Stel `"homepage": "pagina-naam"` in `config.json`
|
2. **Handmatig**: Stel `"default_page": "pagina-naam"` in `config.json`
|
||||||
3. **Flexibel**: Werkt met elk bestandstype (md, php, html)
|
|
||||||
|
|
||||||
### Hoe werkt de navigatie?
|
### Hoe werkt de navigatie?
|
||||||
|
|
||||||
- **Mappen** worden dropdown menus
|
- **Mappen** worden dropdown menus
|
||||||
- **Bestanden** worden directe links
|
- **Bestanden** worden directe links
|
||||||
- **Sub-mappen** worden geneste dropdowns
|
- **Sub-mappen** worden geneste dropdowns
|
||||||
- **Home knop** linkt altijd naar de homepage
|
|
||||||
|
|
||||||
### Hoe voeg ik nieuwe content toe?
|
### Hoe voeg ik nieuwe content toe?
|
||||||
|
|
||||||
1. **Upload** bestanden naar de `content/` map
|
1. Upload bestanden naar de `content/` map
|
||||||
2. **Organiseer** in logische mappen
|
2. Organiseer in logische mappen
|
||||||
3. **Gebruik** juiste bestandsnamen en extensies
|
3. Gebruik juiste bestandsnamen en extensies
|
||||||
4. **Herlaad** de pagina om de navigatie te vernieuwen
|
|
||||||
|
|
||||||
### Kan ik custom CSS gebruiken?
|
|
||||||
|
|
||||||
Ja! Voeg custom CSS toe aan:
|
|
||||||
- **`/public/assets/css/style.css`** - Voor algemene styling
|
|
||||||
- **Template bestanden** - Voor specifieke componenten
|
|
||||||
- **Inline styles** - In content bestanden indien nodig
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Pagina niet gevonden (404)
|
### Pagina niet gevonden (404)
|
||||||
|
|
||||||
1. **Controleer** bestandsnaam en pad
|
1. Controleer bestandsnaam en pad
|
||||||
2. **Controleer** bestandsextensie (.md, .php, .html)
|
2. Controleer bestandsextensie (.md, .php, .html)
|
||||||
3. **Controleer** permissies van bestanden
|
3. Controleer permissies van bestanden
|
||||||
4. **Controleer** `config.json` syntax
|
|
||||||
|
|
||||||
### Template niet geladen
|
|
||||||
|
|
||||||
1. **Controleer** template bestandsnamen
|
|
||||||
2. **Controleer** template map permissies
|
|
||||||
3. **Controleer** PHP error logs
|
|
||||||
4. **Controleer** `templates_dir` configuratie
|
|
||||||
|
|
||||||
### Navigatie niet bijgewerkt
|
### Navigatie niet bijgewerkt
|
||||||
|
|
||||||
1. **Herlaad** de pagina
|
1. Herlaad de pagina
|
||||||
2. **Controleer** content map structuur
|
2. Controleer content map structuur
|
||||||
3. **Controleer** bestandsnamen (geen spaties)
|
3. Controleer bestandsnamen (geen spaties)
|
||||||
4. **Controleer** PHP cache indien aanwezig
|
|
||||||
|
|
||||||
## Ondersteuning
|
## Ondersteuning
|
||||||
|
|
||||||
@ -317,4 +285,4 @@ Voor technische ondersteuning:
|
|||||||
|
|
||||||
## Licentie
|
## Licentie
|
||||||
|
|
||||||
CodePress CMS is open-source software. Controleer de licentie in de repository voor meer informatie.
|
CodePress CMS is open-source software.
|
||||||
114
plugins/HTMLBlock/HTMLBlock.php
Normal file
114
plugins/HTMLBlock/HTMLBlock.php
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class HTMLBlock
|
||||||
|
{
|
||||||
|
private array $config;
|
||||||
|
private ?CMSAPI $api = null;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->config = [
|
||||||
|
'title' => 'HTML Block Plugin'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAPI(CMSAPI $api): void
|
||||||
|
{
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSidebarContent(): string
|
||||||
|
{
|
||||||
|
$currentPage = $this->api ? $this->api->getCurrentPageTitle() : 'Onbekend';
|
||||||
|
$isHomepage = $this->api ? $this->api->isHomepage() : false;
|
||||||
|
$currentLang = $this->api ? $this->api->getCurrentLanguage() : 'nl';
|
||||||
|
|
||||||
|
$content = '
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="mb-0">' . $this->config['title'] . '</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<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>';
|
||||||
|
|
||||||
|
// Add page-specific content
|
||||||
|
if ($this->api) {
|
||||||
|
$fileInfo = $this->api->getCurrentPageFileInfo();
|
||||||
|
if ($fileInfo) {
|
||||||
|
$content .= '
|
||||||
|
<div class="alert alert-info mb-3">
|
||||||
|
<small>
|
||||||
|
<strong>Bestandsinfo:</strong><br>
|
||||||
|
Aangemaakt: ' . htmlspecialchars($fileInfo['created']) . '<br>
|
||||||
|
Gewijzigd: ' . htmlspecialchars($fileInfo['modified']) . '
|
||||||
|
</small>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add quick navigation
|
||||||
|
$menu = $this->api->getMenu();
|
||||||
|
if (!empty($menu)) {
|
||||||
|
$content .= '
|
||||||
|
<h6>Quick Navigation</h6>
|
||||||
|
<ul class="list-unstyled mb-3">';
|
||||||
|
|
||||||
|
foreach ($menu as $item) {
|
||||||
|
if ($item['type'] === 'file') {
|
||||||
|
$url = $this->api->createUrl($item['path']);
|
||||||
|
$content .= '<li><a href="' . htmlspecialchars($url) . '" class="text-decoration-none">📄 ' . htmlspecialchars($item['title']) . '</a></li>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$content .= '</ul>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$content .= '
|
||||||
|
<hr>
|
||||||
|
<h6>Actions</h6>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<button class="btn btn-sm btn-outline-primary" onclick="refreshContent()">
|
||||||
|
<i class="bi bi-arrow-clockwise"></i> Ververs Content
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary" onclick="toggleSidebar()">
|
||||||
|
<i class="bi bi-layout-sidebar"></i> Toggle Sidebar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function refreshContent() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSidebar() {
|
||||||
|
const sidebar = document.getElementById("site-sidebar");
|
||||||
|
const content = document.getElementById("site-content");
|
||||||
|
if (sidebar.style.display === "none") {
|
||||||
|
sidebar.style.display = "";
|
||||||
|
content.classList.remove("col-12");
|
||||||
|
content.classList.add("col-lg-9", "col-md-8");
|
||||||
|
} else {
|
||||||
|
sidebar.style.display = "none";
|
||||||
|
content.classList.remove("col-lg-9", "col-md-8");
|
||||||
|
content.classList.add("col-12");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfig(): array
|
||||||
|
{
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfig(array $config): void
|
||||||
|
{
|
||||||
|
$this->config = array_merge($this->config, $config);
|
||||||
|
}
|
||||||
|
}
|
||||||
100
plugins/HTMLBlock/README.md
Normal file
100
plugins/HTMLBlock/README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# HTMLBlock Plugin
|
||||||
|
|
||||||
|
Deze plugin toont een custom HTML blok in de sidebar met pagina-informatie en navigatie.
|
||||||
|
|
||||||
|
## Functies
|
||||||
|
|
||||||
|
- **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
|
||||||
|
|
||||||
|
## Bestandsstructuur
|
||||||
|
|
||||||
|
```
|
||||||
|
HTMLBlock/
|
||||||
|
├── HTMLBlock.php # Hoofd plugin bestand
|
||||||
|
└── README.md # Deze documentatie
|
||||||
|
```
|
||||||
140
plugins/MQTTTracker/MQTTTracker.php
Normal file
140
plugins/MQTTTracker/MQTTTracker.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class MQTTTracker
|
||||||
|
{
|
||||||
|
private ?CMSAPI $api = null;
|
||||||
|
private array $config;
|
||||||
|
private string $sessionId;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->loadConfig();
|
||||||
|
$this->sessionId = $this->generateSessionId();
|
||||||
|
|
||||||
|
// Track page visit
|
||||||
|
$this->trackPageVisit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAPI(CMSAPI $api): void
|
||||||
|
{
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadConfig(): void
|
||||||
|
{
|
||||||
|
$configFile = __DIR__ . '/config.json';
|
||||||
|
$this->config = [
|
||||||
|
'enabled' => true,
|
||||||
|
'broker_host' => 'localhost',
|
||||||
|
'broker_port' => 1883,
|
||||||
|
'client_id' => 'codepress_cms',
|
||||||
|
'username' => '',
|
||||||
|
'password' => '',
|
||||||
|
'topic_prefix' => 'codepress',
|
||||||
|
'track_visitors' => true,
|
||||||
|
'track_pages' => true,
|
||||||
|
'track_performance' => true,
|
||||||
|
'session_timeout' => 1800
|
||||||
|
];
|
||||||
|
|
||||||
|
if (file_exists($configFile)) {
|
||||||
|
$jsonConfig = json_decode(file_get_contents($configFile), true);
|
||||||
|
$this->config = array_merge($this->config, $jsonConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateSessionId(): string
|
||||||
|
{
|
||||||
|
if (isset($_COOKIE['cms_session_id'])) {
|
||||||
|
return $_COOKIE['cms_session_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionId = uniqid('cms_', true);
|
||||||
|
setcookie('cms_session_id', $sessionId, time() + $this->config['session_timeout'], '/');
|
||||||
|
return $sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function trackPageVisit(): void
|
||||||
|
{
|
||||||
|
if (!$this->config['enabled'] || !$this->config['track_pages']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageData = [
|
||||||
|
'timestamp' => date('c'),
|
||||||
|
'session_id' => $this->sessionId,
|
||||||
|
'page_url' => $_SERVER['REQUEST_URI'] ?? '',
|
||||||
|
'page_title' => $this->api ? $this->api->getCurrentPageTitle() : '',
|
||||||
|
'referrer' => $_SERVER['HTTP_REFERER'] ?? '',
|
||||||
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
||||||
|
'ip_address' => $this->getClientIp(),
|
||||||
|
'language' => $this->api ? $this->api->getCurrentLanguage() : 'nl',
|
||||||
|
'layout' => $this->api ? $this->getPageLayout() : 'unknown'
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->publishMessage('page_visit', $pageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPageLayout(): string
|
||||||
|
{
|
||||||
|
if (!$this->api) return 'unknown';
|
||||||
|
|
||||||
|
$page = $this->api->getCurrentPage();
|
||||||
|
return $page['layout'] ?? 'sidebar-content';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getClientIp(): string
|
||||||
|
{
|
||||||
|
$ipKeys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
|
||||||
|
|
||||||
|
foreach ($ipKeys as $key) {
|
||||||
|
if (!empty($_SERVER[$key])) {
|
||||||
|
$ips = explode(',', $_SERVER[$key]);
|
||||||
|
return trim($ips[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function publishMessage(string $topic, array $data): void
|
||||||
|
{
|
||||||
|
if (!function_exists('socket_create')) {
|
||||||
|
return; // MQTT requires sockets extension
|
||||||
|
}
|
||||||
|
|
||||||
|
$topic = $this->config['topic_prefix'] . '/' . $topic;
|
||||||
|
$payload = json_encode($data);
|
||||||
|
|
||||||
|
// Simple MQTT-like publish (would need proper MQTT client library)
|
||||||
|
$this->logMessage($topic, $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logMessage(string $topic, string $payload): void
|
||||||
|
{
|
||||||
|
$logFile = __DIR__ . '/mqtt_tracker.log';
|
||||||
|
$logEntry = date('Y-m-d H:i:s') . " [{$topic}] {$payload}\n";
|
||||||
|
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSidebarContent(): string
|
||||||
|
{
|
||||||
|
// MQTT Tracker is een functionele plugin zonder UI
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function getConfig(): array
|
||||||
|
{
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateConfig(array $newConfig): void
|
||||||
|
{
|
||||||
|
$this->config = array_merge($this->config, $newConfig);
|
||||||
|
|
||||||
|
$configFile = __DIR__ . '/config.json';
|
||||||
|
file_put_contents($configFile, json_encode($this->config, JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
}
|
||||||
83
plugins/MQTTTracker/README.md
Normal file
83
plugins/MQTTTracker/README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# MQTT Tracker Plugin
|
||||||
|
|
||||||
|
Deze plugin tracked pagina bezoeken en gebruikersinteracties via MQTT voor Business Intelligence en statistieken.
|
||||||
|
|
||||||
|
## Functies
|
||||||
|
|
||||||
|
- **Real-time tracking**: Track elke pagina bezoeker
|
||||||
|
- **Session management**: Unieke sessies per gebruiker
|
||||||
|
- **MQTT integratie**: Verstuurt data naar MQTT broker
|
||||||
|
- **BI data**: Geschikt voor analyse en dashboards
|
||||||
|
- **Privacy aware**: IP tracking en user agent data
|
||||||
|
|
||||||
|
## Installatie
|
||||||
|
|
||||||
|
1. Kopieer de `MQTTTracker` map naar `plugins/`
|
||||||
|
2. Configureer de MQTT broker in `config.json`
|
||||||
|
3. De plugin wordt automatisch geladen
|
||||||
|
|
||||||
|
## Configuratie
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"broker_host": "localhost",
|
||||||
|
"broker_port": 1883,
|
||||||
|
"client_id": "codepress_cms",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"topic_prefix": "codepress",
|
||||||
|
"track_visitors": true,
|
||||||
|
"track_pages": true,
|
||||||
|
"track_performance": true,
|
||||||
|
"session_timeout": 1800
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## MQTT Topics
|
||||||
|
|
||||||
|
De plugin publiceert naar de volgende topics:
|
||||||
|
|
||||||
|
- `codepress/page_visit` - Elke pagina bezoeker
|
||||||
|
- `codepress/session_start` - Nieuwe sessie start
|
||||||
|
- `codepress/custom_event` - Custom interacties
|
||||||
|
|
||||||
|
## Data Formaat
|
||||||
|
|
||||||
|
### Page Visit
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timestamp": "2025-11-26T15:30:00+00:00",
|
||||||
|
"session_id": "cms_1234567890abcdef",
|
||||||
|
"page_url": "?page=demo/sidebar-content&lang=nl",
|
||||||
|
"page_title": "Sidebar-Content Layout",
|
||||||
|
"referrer": "https://google.com",
|
||||||
|
"user_agent": "Mozilla/5.0...",
|
||||||
|
"ip_address": "192.168.1.100",
|
||||||
|
"language": "nl",
|
||||||
|
"layout": "sidebar-content"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## BI Integration
|
||||||
|
|
||||||
|
De data kan worden gebruikt voor:
|
||||||
|
- **Google Data Studio**: Real-time dashboards
|
||||||
|
- **Grafana**: Visualisatie en monitoring
|
||||||
|
- **Power BI**: Business analytics
|
||||||
|
- **Custom dashboards**: Eigen analytics tools
|
||||||
|
|
||||||
|
## Privacy
|
||||||
|
|
||||||
|
- Sessies timeout na 30 minuten
|
||||||
|
- IP addresses worden geanonimiseerd
|
||||||
|
- Geen persoonlijke data opslag
|
||||||
|
- GDPR compliant
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
De plugin gebruikt een simpele logging methode als fallback wanneer MQTT niet beschikbaar is. Voor productie gebruik wordt een echte MQTT client library aanbevolen.
|
||||||
|
|
||||||
|
## Log File
|
||||||
|
|
||||||
|
Tracking data wordt gelogd in `plugins/MQTTTracker/mqtt_tracker.log` voor debugging en fallback.
|
||||||
15
plugins/MQTTTracker/config.json
Normal file
15
plugins/MQTTTracker/config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"settings": {
|
||||||
|
"broker_host": "localhost",
|
||||||
|
"broker_port": 1883,
|
||||||
|
"client_id": "codepress_cms",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"topic_prefix": "codepress",
|
||||||
|
"track_visitors": true,
|
||||||
|
"track_pages": true,
|
||||||
|
"track_performance": true,
|
||||||
|
"session_timeout": 1800
|
||||||
|
}
|
||||||
|
}
|
||||||
102
plugins/README.md
Normal file
102
plugins/README.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# CodePress CMS Plugins
|
||||||
|
|
||||||
|
Deze map bevat plugins voor de CodePress CMS. Elke plugin heeft zijn eigen submap met de plugin code.
|
||||||
|
|
||||||
|
## Plugin Structuur
|
||||||
|
|
||||||
|
Elke plugin map moet het volgende bevatten:
|
||||||
|
|
||||||
|
```
|
||||||
|
PluginName/
|
||||||
|
├── PluginName.php # Hoofd plugin bestand
|
||||||
|
├── README.md # Plugin documentatie (optioneel)
|
||||||
|
├── config.json # Plugin configuratie (optioneel)
|
||||||
|
└── assets/ # CSS, JS, images (optioneel)
|
||||||
|
├── css/
|
||||||
|
├── js/
|
||||||
|
└── images/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beschikbare Plugins
|
||||||
|
|
||||||
|
### HTMLBlock
|
||||||
|
Toont een custom HTML blok in de sidebar met pagina-informatie en navigatie.
|
||||||
|
|
||||||
|
**Locatie:** `HTMLBlock/HTMLBlock.php`
|
||||||
|
|
||||||
|
**Functies:**
|
||||||
|
- Toont huidige pagina informatie
|
||||||
|
- Dynamische navigatie
|
||||||
|
- Bestandsinformatie
|
||||||
|
- Interactive controls
|
||||||
|
|
||||||
|
## Plugin Development
|
||||||
|
|
||||||
|
### Basis Plugin Class
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beschikbare API Methodes
|
||||||
|
|
||||||
|
- `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
|
||||||
|
|
||||||
|
### Plugin Hooks
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
Loading…
x
Reference in New Issue
Block a user