Merge development into main - Version 1.5.0 release
Merge includes: - Version bump to 1.5.0 with comprehensive changelog - Fixed guide template variable replacement bug - Complete guide documentation rewrite - Plugin system implementation (HTMLBlock, MQTTTracker) - Enhanced bilingual support throughout the system - Improved template system and layouts - Enhanced security and code quality improvements Resolves merge conflicts in README files, keeping version 1.5.0.
This commit is contained in:
commit
f685c2490a
@ -4,7 +4,7 @@
|
||||
|
||||
A lightweight, file-based content management system built with PHP.
|
||||
|
||||
**Version:** 1.0.0 | **License:** AGPL v3 / Commercial
|
||||
**Version:** 1.5.0 | **License:** AGPL v3 / Commercial
|
||||
|
||||
## ✨ Features
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
Een lichtgewicht, file-based content management systeem gebouwd met PHP.
|
||||
|
||||
**Versie:** 1.0.0 | **Licentie:** AGPL v3 / Commercial
|
||||
**Versie:** 1.5.0 | **Licentie:** AGPL v3 / Commercial
|
||||
|
||||
## ✨ Features
|
||||
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
"header_color": "#0a369d",
|
||||
"header_font_color": "#ffffff",
|
||||
"navigation_color": "#2754b4",
|
||||
"navigation_font_color": "#ffffff"
|
||||
"navigation_font_color": "#ffffff",
|
||||
"sidebar_background": "#f8f9fa",
|
||||
"sidebar_border": "#dee2e6"
|
||||
},
|
||||
"language": {
|
||||
"default": "nl",
|
||||
@ -19,9 +21,8 @@
|
||||
"keywords": "cms, php, content management, file-based"
|
||||
},
|
||||
"author": {
|
||||
"name": "Edwin Noorlander",
|
||||
"website": "https://noorlander.info",
|
||||
"git": "https://git.noorlander.info/E.Noorlander/CodePress.git"
|
||||
"name": "E. Noorlander",
|
||||
"website": "https://noorlander.info"
|
||||
},
|
||||
"features": {
|
||||
"auto_link_pages": true,
|
||||
|
||||
@ -21,11 +21,12 @@
|
||||
* @license MIT
|
||||
*/
|
||||
class CodePressCMS {
|
||||
private $config;
|
||||
public $config;
|
||||
public $currentLanguage;
|
||||
public $searchResults = [];
|
||||
private $menu = [];
|
||||
private $searchResults = [];
|
||||
private $currentLanguage;
|
||||
private $translations = [];
|
||||
private $pluginManager;
|
||||
|
||||
/**
|
||||
* Constructor - Initialize the CMS with configuration
|
||||
@ -43,6 +44,14 @@ class CodePressCMS {
|
||||
|
||||
$this->currentLanguage = $this->getCurrentLanguage();
|
||||
$this->translations = $this->loadTranslations($this->currentLanguage);
|
||||
|
||||
// Initialize plugin manager
|
||||
require_once __DIR__ . '/../plugin/PluginManager.php';
|
||||
require_once __DIR__ . '/../plugin/CMSAPI.php';
|
||||
$this->pluginManager = new PluginManager(__DIR__ . '/../../../plugins');
|
||||
$api = new CMSAPI($this);
|
||||
$this->pluginManager->setAPI($api);
|
||||
|
||||
$this->buildMenu();
|
||||
|
||||
if (isset($_GET['search'])) {
|
||||
@ -67,7 +76,7 @@ class CodePressCMS {
|
||||
*
|
||||
* @return array Available languages with their codes and names
|
||||
*/
|
||||
private function getAvailableLanguages() {
|
||||
public function getAvailableLanguages() {
|
||||
$langDir = __DIR__ . '/../../lang/';
|
||||
$languages = [];
|
||||
|
||||
@ -444,16 +453,59 @@ class CodePressCMS {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse metadata from content
|
||||
*
|
||||
* @param string $content Raw content
|
||||
* @return array Parsed metadata and content without meta block
|
||||
*/
|
||||
private function parseMetadata($content) {
|
||||
$metadata = [];
|
||||
$contentWithoutMeta = $content;
|
||||
|
||||
// Check for YAML frontmatter (--- at start and end)
|
||||
if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) {
|
||||
$metaContent = $matches[1];
|
||||
$contentWithoutMeta = $matches[2];
|
||||
|
||||
// Parse YAML-like metadata
|
||||
$lines = explode("\n", $metaContent);
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, ':') !== false) {
|
||||
list($key, $value) = explode(':', $line, 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value, ' "\'');
|
||||
|
||||
// Handle boolean values
|
||||
if ($value === 'true') $value = true;
|
||||
elseif ($value === 'false') $value = false;
|
||||
|
||||
$metadata[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'metadata' => $metadata,
|
||||
'content' => $contentWithoutMeta
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Markdown content to HTML using League CommonMark
|
||||
*
|
||||
* @param string $content Raw Markdown content
|
||||
* @return array Parsed content with title and body
|
||||
*/
|
||||
private function parseMarkdown($content, $actualFilePath = '') {
|
||||
// Extract title from first H1
|
||||
$title = '';
|
||||
if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
|
||||
public function parseMarkdown($content, $actualFilePath = '') {
|
||||
// Parse metadata first
|
||||
$parsed = $this->parseMetadata($content);
|
||||
$metadata = $parsed['metadata'];
|
||||
$content = $parsed['content'];
|
||||
|
||||
// Extract title from first H1 or metadata
|
||||
$title = $metadata['title'] ?? '';
|
||||
if (empty($title) && preg_match('/^#\s+(.+)$/m', $content, $matches)) {
|
||||
$title = trim($matches[1]);
|
||||
}
|
||||
|
||||
@ -495,8 +547,10 @@ class CodePressCMS {
|
||||
$body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
|
||||
|
||||
return [
|
||||
'title' => $cleanName ?: 'Untitled',
|
||||
'content' => $body
|
||||
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||
'content' => $body,
|
||||
'metadata' => $metadata,
|
||||
'layout' => $metadata['layout'] ?? 'sidebar-content'
|
||||
];
|
||||
}
|
||||
|
||||
@ -544,7 +598,7 @@ class CodePressCMS {
|
||||
*
|
||||
* @return array Associative array of page paths to titles
|
||||
*/
|
||||
private function getAllPageTitles() {
|
||||
public function getAllPageTitles() {
|
||||
$pages = [];
|
||||
$this->scanForPageTitles($this->config['content_dir'], '', $pages);
|
||||
return $pages;
|
||||
@ -708,19 +762,36 @@ class CodePressCMS {
|
||||
* @return array Parsed content with title and body
|
||||
*/
|
||||
private function parsePHP($filePath) {
|
||||
// Read file content first to extract metadata
|
||||
$fileContent = file_get_contents($filePath);
|
||||
$parsed = $this->parseMetadata($fileContent);
|
||||
$metadata = $parsed['metadata'];
|
||||
|
||||
// Extract title from metadata or PHP variables
|
||||
$title = $metadata['title'] ?? '';
|
||||
|
||||
ob_start();
|
||||
// Make metadata available to the included file
|
||||
$pageMetadata = $metadata;
|
||||
include $filePath;
|
||||
$content = ob_get_clean();
|
||||
|
||||
// Remove any remaining metadata from PHP output
|
||||
$content = preg_replace('/^---\s*\n.*?\n---\s*\n/s', '', $content);
|
||||
|
||||
// Remove metadata from content if it was included
|
||||
$parsed = $this->parseMetadata($content);
|
||||
$content = $parsed['content'];
|
||||
|
||||
// Extract filename for title
|
||||
$filename = basename($filePath);
|
||||
$cleanName = $this->formatDisplayName($filename);
|
||||
|
||||
return [
|
||||
'title' => $cleanName ?: 'Untitled',
|
||||
'content' => $content
|
||||
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||
'content' => $content,
|
||||
'metadata' => $metadata,
|
||||
'layout' => $metadata['layout'] ?? 'sidebar-content'
|
||||
];
|
||||
}
|
||||
|
||||
@ -731,13 +802,30 @@ class CodePressCMS {
|
||||
* @return array Parsed content with title and body
|
||||
*/
|
||||
private function parseHTML($content, $actualFilePath = '') {
|
||||
// Parse metadata first
|
||||
$parsed = $this->parseMetadata($content);
|
||||
$metadata = $parsed['metadata'];
|
||||
$content = $parsed['content'];
|
||||
|
||||
// Extract title from metadata or HTML tags
|
||||
$title = $metadata['title'] ?? '';
|
||||
if (empty($title)) {
|
||||
if (preg_match('/<title>(.*?)<\/title>/i', $content, $matches)) {
|
||||
$title = trim(strip_tags($matches[1]));
|
||||
} elseif (preg_match('/<h1[^>]*>(.*?)<\/h1>/i', $content, $matches)) {
|
||||
$title = trim(strip_tags($matches[1]));
|
||||
}
|
||||
}
|
||||
|
||||
// Extract filename for title
|
||||
$filename = basename($actualFilePath);
|
||||
$cleanName = $this->formatDisplayName($filename);
|
||||
|
||||
return [
|
||||
'title' => $cleanName ?: 'Untitled',
|
||||
'content' => $content
|
||||
'title' => $title ?: $cleanName ?: 'Untitled',
|
||||
'content' => $content,
|
||||
'metadata' => $metadata,
|
||||
'layout' => $metadata['layout'] ?? 'sidebar-content'
|
||||
];
|
||||
}
|
||||
|
||||
@ -746,7 +834,7 @@ class CodePressCMS {
|
||||
*
|
||||
* @return bool True if content directory is empty or doesn't exist
|
||||
*/
|
||||
private function isContentDirEmpty() {
|
||||
public function isContentDirEmpty() {
|
||||
$contentDir = $this->config['content_dir'];
|
||||
if (!is_dir($contentDir)) {
|
||||
return true;
|
||||
@ -772,12 +860,51 @@ class CodePressCMS {
|
||||
}
|
||||
|
||||
$content = file_get_contents($guideFile);
|
||||
$result = $this->parseMarkdown($content);
|
||||
|
||||
// Parse metadata first
|
||||
$parsed = $this->parseMetadata($content);
|
||||
$metadata = $parsed['metadata'];
|
||||
$contentWithoutMeta = $parsed['content'];
|
||||
|
||||
// Include autoloader
|
||||
require_once __DIR__ . '/../../../vendor/autoload.php';
|
||||
|
||||
// Configure CommonMark environment
|
||||
$config = [
|
||||
'html_input' => 'strip',
|
||||
'allow_unsafe_links' => false,
|
||||
'max_nesting_level' => 100,
|
||||
];
|
||||
|
||||
// Create environment with extensions
|
||||
$environment = new \League\CommonMark\Environment\Environment($config);
|
||||
$environment->addExtension(new \League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension());
|
||||
$environment->addExtension(new \League\CommonMark\Extension\Autolink\AutolinkExtension());
|
||||
$environment->addExtension(new \League\CommonMark\Extension\Strikethrough\StrikethroughExtension());
|
||||
$environment->addExtension(new \League\CommonMark\Extension\Table\TableExtension());
|
||||
$environment->addExtension(new \League\CommonMark\Extension\TaskList\TaskListExtension());
|
||||
|
||||
// Create converter
|
||||
$converter = new \League\CommonMark\MarkdownConverter($environment);
|
||||
|
||||
// Convert to HTML
|
||||
$body = $converter->convert($contentWithoutMeta)->getContent();
|
||||
|
||||
// Extract title from metadata or first H1
|
||||
$title = $metadata['title'] ?? '';
|
||||
if (empty($title) && preg_match('/^#\s+(.+)$/m', $contentWithoutMeta, $matches)) {
|
||||
$title = trim($matches[1]);
|
||||
}
|
||||
|
||||
// Set special title for guide
|
||||
$result['title'] = $this->t('manual') . ' - CodePress CMS';
|
||||
$title = $this->t('manual') . ' - CodePress CMS';
|
||||
|
||||
return $result;
|
||||
return [
|
||||
'title' => $title,
|
||||
'content' => $body,
|
||||
'metadata' => $metadata,
|
||||
'layout' => $metadata['layout'] ?? 'content'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -912,12 +1039,21 @@ class CodePressCMS {
|
||||
// Get homepage title
|
||||
$homepageTitle = $this->getHomepageTitle();
|
||||
|
||||
// Get sidebar content from plugins
|
||||
$sidebarContent = $this->pluginManager->getSidebarContent();
|
||||
|
||||
// Get layout from page metadata
|
||||
$layout = $page['layout'] ?? 'sidebar-content';
|
||||
|
||||
// Prepare template data
|
||||
$templateData = [
|
||||
'site_title' => $this->config['site_title'],
|
||||
'page_title' => htmlspecialchars($page['title']),
|
||||
|
||||
'content' => $page['content'],
|
||||
'sidebar_content' => $sidebarContent,
|
||||
'layout' => $layout,
|
||||
'page_metadata' => $page['metadata'] ?? [],
|
||||
'search_query' => isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '',
|
||||
'menu' => $this->renderMenu($menu),
|
||||
'breadcrumb' => $breadcrumb,
|
||||
@ -930,7 +1066,7 @@ class CodePressCMS {
|
||||
'lang_switch_url' => isset($_GET['guide']) ? '&guide' : '&page=' . $this->config['default_page'],
|
||||
'author_name' => $this->config['author']['name'] ?? 'CodePress Developer',
|
||||
'author_website' => $this->config['author']['website'] ?? '#',
|
||||
'author_git' => $this->config['author']['git'] ?? '#',
|
||||
'author_git' => 'https://git.noorlander.info/E.Noorlander',
|
||||
'seo_description' => $this->config['seo']['description'] ?? 'CodePress CMS - Lightweight file-based content management system',
|
||||
'seo_keywords' => $this->config['seo']['keywords'] ?? 'cms, php, content management, file-based',
|
||||
'cms_version' => isset($this->config['version_info']) ? 'v' . $this->config['version_info']['version'] : '',
|
||||
@ -939,6 +1075,8 @@ class CodePressCMS {
|
||||
'header_font_color' => $this->config['theme']['header_font_color'] ?? '#ffffff',
|
||||
'navigation_color' => $this->config['theme']['navigation_color'] ?? '#f8f9fa',
|
||||
'navigation_font_color' => $this->config['theme']['navigation_font_color'] ?? '#000000',
|
||||
'sidebar_background' => $this->config['theme']['sidebar_background'] ?? '#f8f9fa',
|
||||
'sidebar_border' => $this->config['theme']['sidebar_border'] ?? '#dee2e6',
|
||||
// Language
|
||||
'current_lang' => $this->currentLanguage,
|
||||
'current_lang_upper' => strtoupper($this->currentLanguage),
|
||||
@ -967,17 +1105,20 @@ class CodePressCMS {
|
||||
't_page_not_found' => $this->t('page_not_found'),
|
||||
't_page_not_found_text' => $this->t('page_not_found_text'),
|
||||
't_mappen' => $this->t('mappen'),
|
||||
't_paginas' => $this->t('paginas')
|
||||
't_paginas' => $this->t('paginas'),
|
||||
't_author_website' => $this->t('author_website'),
|
||||
't_author_git' => $this->t('author_git')
|
||||
];
|
||||
|
||||
// File info for footer
|
||||
if (isset($page['file_info'])) {
|
||||
$templateData['file_info'] = $this->t('created') . ': ' . htmlspecialchars($page['file_info']['created']) .
|
||||
' | ' . $this->t('modified') . ': ' . htmlspecialchars($page['file_info']['modified']);
|
||||
$templateData['file_info_block'] = '<span class="file-details"> | ' . $templateData['file_info'] . '</span>';
|
||||
$templateData['created'] = htmlspecialchars($page['file_info']['created']);
|
||||
$templateData['modified'] = htmlspecialchars($page['file_info']['modified']);
|
||||
$templateData['file_info_block'] = true;
|
||||
} else {
|
||||
$templateData['file_info'] = '';
|
||||
$templateData['file_info_block'] = '';
|
||||
$templateData['created'] = '';
|
||||
$templateData['modified'] = '';
|
||||
$templateData['file_info_block'] = false;
|
||||
}
|
||||
|
||||
|
||||
@ -1025,7 +1166,7 @@ class CodePressCMS {
|
||||
*
|
||||
* @return string Breadcrumb HTML
|
||||
*/
|
||||
private function generateBreadcrumb() {
|
||||
public function generateBreadcrumb() {
|
||||
if (isset($_GET['search'])) {
|
||||
return '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="?page=' . $this->config['default_page'] . '&lang=' . $this->currentLanguage . '"></a></li><li class="breadcrumb-item"> > </li><li class="breadcrumb-item active">' . $this->t('search') . '</li></ol></nav>';
|
||||
}
|
||||
|
||||
@ -37,6 +37,16 @@ class SimpleTemplate {
|
||||
// Handle partial includes first ({{>partial}})
|
||||
$template = preg_replace_callback('/{{>([^}]+)}}/', [$this, 'replacePartial'], $template);
|
||||
|
||||
// Handle equal conditionals first
|
||||
$template = preg_replace_callback('/{{#equal\s+(\w+)\s+["\']([^"\']+)["\']}}(.*?){{\/equal}}/s', function($matches) {
|
||||
$key = $matches[1];
|
||||
$expectedValue = $matches[2];
|
||||
$content = $matches[3];
|
||||
|
||||
$actualValue = $this->data[$key] ?? '';
|
||||
return ($actualValue === $expectedValue) ? $content : '';
|
||||
}, $template);
|
||||
|
||||
// Handle conditional blocks
|
||||
foreach ($this->data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
|
||||
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_text' => 'The page you are looking for does not exist.',
|
||||
'mappen' => 'Folders',
|
||||
'paginas' => 'Pages'
|
||||
'paginas' => 'Pages',
|
||||
'author_website' => 'Author website',
|
||||
'author_git' => 'Author Git',
|
||||
'plugins' => 'Plugins',
|
||||
'templates' => 'Templates',
|
||||
'layouts' => 'Layouts',
|
||||
'sidebar_content' => 'Sidebar + Content',
|
||||
'content_only' => 'Content Only',
|
||||
'sidebar_only' => 'Sidebar Only',
|
||||
'content_sidebar' => 'Content + Sidebar',
|
||||
'plugin_development' => 'Plugin Development',
|
||||
'template_system' => 'Template System',
|
||||
'mqtt_tracking' => 'MQTT Tracking',
|
||||
'real_time_analytics' => 'Real-time Analytics'
|
||||
];
|
||||
@ -22,5 +22,18 @@ return [
|
||||
'page_not_found' => 'Pagina niet gevonden',
|
||||
'page_not_found_text' => 'De pagina die u zoekt bestaat niet.',
|
||||
'mappen' => 'Mappen',
|
||||
'paginas' => 'Pagina\'s'
|
||||
'paginas' => 'Pagina\'s',
|
||||
'author_website' => 'Auteur website',
|
||||
'author_git' => 'Auteur Git',
|
||||
'plugins' => 'Plugins',
|
||||
'templates' => 'Templates',
|
||||
'layouts' => 'Layouts',
|
||||
'sidebar_content' => 'Sidebar + Content',
|
||||
'content_only' => 'Alleen Content',
|
||||
'sidebar_only' => 'Alleen Sidebar',
|
||||
'content_sidebar' => 'Content + Sidebar',
|
||||
'plugin_development' => 'Plugin Ontwikkeling',
|
||||
'template_system' => 'Template Systeem',
|
||||
'mqtt_tracking' => 'MQTT Tracking',
|
||||
'real_time_analytics' => 'Real-time Analytics'
|
||||
];
|
||||
@ -1,20 +1,39 @@
|
||||
<footer class="bg-light border-top py-3">
|
||||
<footer class="bg-light border-top py-1">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="file-info">
|
||||
<i class="bi bi-file-text"></i>
|
||||
<span class="page-title" title="{{page_title}}">{{page_title}}</span>
|
||||
{{{file_info_block}}}
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center">
|
||||
<div class="file-info mb-2 mb-md-0">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-file-text footer-icon" title="{{t_file_details}}: {{page_title}}"></i>
|
||||
<span class="page-title d-none d-lg-inline" title="{{page_title}}">{{page_title}}</span>
|
||||
{{#file_info_block}}
|
||||
<span class="ms-2">
|
||||
<i class="bi bi-calendar-plus footer-icon" title="{{t_created}}: {{created}}"></i>
|
||||
<span class="file-created">{{created}}</span>
|
||||
<i class="bi bi-calendar-check ms-1 footer-icon" title="{{t_modified}}: {{modified}}"></i>
|
||||
<span class="file-modified">{{modified}}</span>
|
||||
</span>
|
||||
{{/file_info_block}}
|
||||
</small>
|
||||
</div>
|
||||
<div class="site-info">
|
||||
<small class="text-muted">
|
||||
<a href="?guide&lang={{current_lang}}" class="guide-link" title="{{t_guide}}">
|
||||
<a href="?guide&lang={{current_lang}}" class="footer-icon guide" title="{{t_guide}}">
|
||||
<i class="bi bi-book"></i>
|
||||
</a>
|
||||
<span class="ms-2">|</span>
|
||||
{{t_powered_by}} <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a> {{cms_version}}
|
||||
<span class="ms-1">|</span>
|
||||
<a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener" class="footer-icon cms" title="{{t_powered_by}} CodePress CMS {{cms_version}}">
|
||||
<i class="bi bi-cpu"></i>
|
||||
</a>
|
||||
<span class="ms-1">|</span>
|
||||
<a href="{{author_website}}" target="_blank" rel="noopener" class="footer-icon website" title="{{t_author_website}}">
|
||||
<i class="bi bi-globe"></i>
|
||||
</a>
|
||||
<span class="ms-1">|</span>
|
||||
<a href="{{author_git}}" target="_blank" rel="noopener" class="footer-icon git" title="{{t_author_git}}">
|
||||
<i class="bi bi-git"></i>
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -34,6 +34,8 @@
|
||||
--header-font: {{header_font_color}};
|
||||
--nav-bg: {{navigation_color}};
|
||||
--nav-font: {{navigation_font_color}};
|
||||
--sidebar-bg: {{sidebar_background}};
|
||||
--sidebar-border: {{sidebar_border}};
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
@ -151,14 +153,112 @@
|
||||
background-color: rgba(255,255,255,0.2) !important;
|
||||
border-bottom: 2px solid var(--nav-font) !important;
|
||||
}
|
||||
|
||||
/* Sidebar styling */
|
||||
.sidebar-column {
|
||||
background-color: var(--sidebar-bg) !important;
|
||||
border-right: 1px solid var(--sidebar-border) !important;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.content-column {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 2rem;
|
||||
padding-bottom: 80px !important;
|
||||
}
|
||||
|
||||
/* Ensure full height layout */
|
||||
.main-content {
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar-column {
|
||||
border-right: none !important;
|
||||
border-top: 1px solid var(--sidebar-border) !important;
|
||||
min-height: auto;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.content-column {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
min-height: auto;
|
||||
padding-bottom: 2rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet and mobile: sidebar below content */
|
||||
@media (max-width: 991.98px) {
|
||||
.sidebar-column {
|
||||
order: 2 !important;
|
||||
}
|
||||
|
||||
.content-column {
|
||||
order: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Footer icon hover effects */
|
||||
.footer-icon {
|
||||
color: #6c757d;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease-in-out;
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.footer-icon:hover {
|
||||
color: #0d6efd;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.footer-icon:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Specific icon hover colors */
|
||||
.footer-icon.guide:hover {
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.footer-icon.cms:hover {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.footer-icon.git:hover {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.footer-icon.website:hover {
|
||||
color: #fd7e14;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header id="site-header">
|
||||
{{>header}}
|
||||
</header>
|
||||
|
||||
<nav id="site-navigation">
|
||||
{{>navigation}}
|
||||
</nav>
|
||||
|
||||
<div class="breadcrumb-section bg-light border-bottom">
|
||||
<div id="site-breadcrumb" class="breadcrumb-section bg-light border-bottom">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 py-2">
|
||||
@ -168,17 +268,87 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid main-content" style="padding-bottom: 80px;">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<main class="col-12">
|
||||
<main id="site-main" class="main-content" style="padding: 0;">
|
||||
{{#sidebar_content}}
|
||||
{{#equal layout "sidebar-content"}}
|
||||
<div class="row g-0">
|
||||
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column order-2 order-md-1">
|
||||
<div class="sidebar h-100">
|
||||
{{{sidebar_content}}}
|
||||
</div>
|
||||
</aside>
|
||||
<section id="site-content" class="col-lg-9 col-md-8 content-column order-1 order-md-2">
|
||||
<div class="content-wrapper p-4">
|
||||
{{>content_template}}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{/equal}}
|
||||
|
||||
{{#equal layout "content"}}
|
||||
<div class="container">
|
||||
<section id="site-content" class="col-12">
|
||||
<div class="content-wrapper p-4">
|
||||
{{>content_template}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{/equal}}
|
||||
|
||||
{{#equal layout "sidebar"}}
|
||||
<div class="container-fluid">
|
||||
<aside id="site-sidebar" class="col-12 sidebar-column">
|
||||
<div class="sidebar">
|
||||
{{{sidebar_content}}}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{{/equal}}
|
||||
|
||||
{{#equal layout "content-sidebar"}}
|
||||
<div class="row g-0">
|
||||
<section id="site-content" class="col-lg-9 col-md-8 content-column order-1">
|
||||
<div class="content-wrapper p-4">
|
||||
{{>content_template}}
|
||||
</div>
|
||||
</section>
|
||||
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column order-2">
|
||||
<div class="sidebar h-100">
|
||||
{{{sidebar_content}}}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{{/equal}}
|
||||
|
||||
{{#equal layout "content-sidebar-reverse"}}
|
||||
<div class="row g-0 flex-row-reverse">
|
||||
<section id="site-content" class="col-lg-9 col-md-8 content-column">
|
||||
<div class="content-wrapper p-4">
|
||||
{{>content_template}}
|
||||
</div>
|
||||
</section>
|
||||
<aside id="site-sidebar" class="col-lg-3 col-md-4 sidebar-column">
|
||||
<div class="sidebar h-100">
|
||||
{{{sidebar_content}}}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{{/equal}}
|
||||
{{/sidebar_content}}
|
||||
{{^sidebar_content}}
|
||||
<div class="container">
|
||||
<section id="site-content" class="col-12">
|
||||
<div class="content-wrapper p-4">
|
||||
{{>content_template}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{/sidebar_content}}
|
||||
</main>
|
||||
|
||||
<footer id="site-footer">
|
||||
{{>footer}}
|
||||
</footer>
|
||||
|
||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/assets/js/app.js"></script>
|
||||
|
||||
@ -1,191 +1,68 @@
|
||||
# CodePress CMS Guide
|
||||
|
||||
## Welcome to CodePress CMS
|
||||
## Welcome to CodePress
|
||||
|
||||
CodePress is a lightweight, file-based Content Management System built with PHP and Bootstrap.
|
||||
|
||||
### Table of Contents
|
||||
## Features
|
||||
|
||||
1. [Getting Started](#getting-started)
|
||||
2. [Content Management](#content-management)
|
||||
3. [Templates](#templates)
|
||||
4. [Configuration](#configuration)
|
||||
### 🏠 Navigation
|
||||
- Tab-style navigation with Bootstrap styling
|
||||
- Dropdown menus for folders and sub-folders
|
||||
- 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
|
||||
- PHP 8.4+
|
||||
- Web server (Apache/Nginx)
|
||||
- Modern web browser
|
||||
- Write permissions for content directory
|
||||
### 🧭 Configuration
|
||||
- **JSON configuration** in `config.json`
|
||||
- Dynamic homepage setting
|
||||
- SEO settings (description, keywords)
|
||||
- Author information with links
|
||||
- Theme configuration with colors
|
||||
- Language settings
|
||||
- Feature toggles
|
||||
|
||||
### Installation
|
||||
1. Clone or download the CodePress files
|
||||
### 🎨 Layout & Design
|
||||
- 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
|
||||
3. Make sure the `content/` directory is writable
|
||||
4. Navigate to your website in the browser
|
||||
3. Make sure `content/` directory is writable
|
||||
4. Navigate to your website in browser
|
||||
|
||||
### Basic 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}
|
||||
## Configuration
|
||||
|
||||
### Basic Settings
|
||||
|
||||
Edit `config.json` in your project root:
|
||||
|
||||
```json
|
||||
\`\`\`json
|
||||
{
|
||||
"site_title": "Your Website Name",
|
||||
"content_dir": "content",
|
||||
@ -199,12 +76,13 @@ Edit `config.json` in your project root:
|
||||
"header_color": "#0a369d",
|
||||
"header_font_color": "#ffffff",
|
||||
"navigation_color": "#2754b4",
|
||||
"navigation_font_color": "#ffffff"
|
||||
"navigation_font_color": "#ffffff",
|
||||
"sidebar_background": "#f8f9fa",
|
||||
"sidebar_border": "#dee2e6"
|
||||
},
|
||||
"author": {
|
||||
"name": "Your Name",
|
||||
"website": "https://yourwebsite.com",
|
||||
"git": "https://github.com/youruser/codepress"
|
||||
"website": "https://yourwebsite.com"
|
||||
},
|
||||
"seo": {
|
||||
"description": "Your website description",
|
||||
@ -216,70 +94,232 @@ Edit `config.json` in your project root:
|
||||
"breadcrumbs_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
\`\`\`
|
||||
|
||||
### SEO Friendly URLs
|
||||
CodePress automatically generates clean URLs:
|
||||
- `home.md` → `?page=home`
|
||||
- `blog/article.md` → `?page=blog/article`
|
||||
- `nl.page.md` → `?page=nl.page&lang=nl`
|
||||
## Content Management
|
||||
|
||||
### Language Support
|
||||
- **Browser detection**: Automatic language detection
|
||||
- **URL switching**: `?lang=en` or `?lang=nl`
|
||||
### 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
|
||||
|
||||
#### 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`
|
||||
- **Directory precedence**: Directories take precedence over files
|
||||
- **Display names**: `file-name.md` displays as "File Name" in menus
|
||||
|
||||
### Search Functionality
|
||||
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
|
||||
## Templates
|
||||
|
||||
### Directory Listings
|
||||
- **Auto-generation**: `?page=directory` shows directory contents
|
||||
- **File information**: Creation/modification dates and sizes
|
||||
- **Navigation**: Links to files and subdirectories
|
||||
### Template Variables
|
||||
|
||||
#### 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
|
||||
|
||||
### Page Organization
|
||||
|
||||
- Use subdirectories for categories
|
||||
- Give each directory an `index.md` for an overview page
|
||||
- Keep file names short and descriptive
|
||||
- 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
|
||||
|
||||
- Use clear headings (H1, H2, H3)
|
||||
- Add descriptive meta information
|
||||
- Use internal links for better navigation
|
||||
|
||||
### Security
|
||||
- Keep your CodePress installation updated
|
||||
- Restrict write permissions on the `content/` directory
|
||||
- Use HTTPS when possible
|
||||
## Troubleshooting
|
||||
|
||||
---
|
||||
### Common Issues
|
||||
|
||||
## Support
|
||||
|
||||
### Troubleshooting
|
||||
- **Empty pages**: Check file permissions
|
||||
- **Template errors**: Verify template syntax
|
||||
- **404 errors**: Check file names and paths
|
||||
- **Navigation not updated**: Reload the page
|
||||
|
||||
## Support
|
||||
|
||||
### More Information
|
||||
|
||||
- Documentation: [CodePress GitHub](https://git.noorlander.info/E.Noorlander/CodePress.git)
|
||||
- Issues and feature requests: GitHub Issues
|
||||
|
||||
|
||||
@ -2,84 +2,70 @@
|
||||
|
||||
## 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
|
||||
|
||||
### 🏠 **Navigatie**
|
||||
- **Tab-style navigatie** met Bootstrap styling
|
||||
- **Dropdown menus** voor mappen en sub-mappen
|
||||
- **Home knop** met icoon die linkt naar de ingestelde homepage
|
||||
- **Automatische menu generatie** op basis van content structuur
|
||||
- **Responsive design** voor mobiele apparaten
|
||||
- **Breadcrumb navigatie** met home icoon en pad weergave
|
||||
- **Active state marking** voor huidige pagina in menu
|
||||
### 🏠 Navigatie
|
||||
- Tab-style navigatie met Bootstrap styling
|
||||
- Dropdown menus voor mappen en sub-mappen
|
||||
- Home knop met icoon
|
||||
- Automatische menu generatie
|
||||
- Responsive design
|
||||
- Breadcrumb navigatie
|
||||
- Active state marking
|
||||
|
||||
### 📄 **Content Types**
|
||||
- **Markdown (.md)** - Met CommonMark ondersteuning en extensies (autolink, strikethrough, tables, task lists)
|
||||
- **PHP (.php)** - Voor dynamische content en functionaliteit
|
||||
- **HTML (.html)** - Voor statische HTML pagina's
|
||||
- **Directory listings** - Automatische generatie van directory overzichten
|
||||
- **Language-specific content** - `nl.` en `en.` prefix voor meertalige content
|
||||
- **Automatische template selectie** op basis van bestandstype
|
||||
### 📄 Content Types
|
||||
- **Markdown (.md)** - CommonMark ondersteuning
|
||||
- **PHP (.php)** - Dynamische content
|
||||
- **HTML (.html)** - Statische HTML pagina's
|
||||
- **Directory listings** - Automatische directory overzichten
|
||||
- **Language-specific content** - `nl.` en `en.` prefix
|
||||
|
||||
### 🔍 **Zoekfunctionaliteit**
|
||||
- **Volledige tekst zoek** door alle content bestanden
|
||||
- **Resultaten met snippets** en highlighting
|
||||
- **Directe navigatie** naar gevonden pagina's
|
||||
- **SEO-vriendelijke** zoekresultaten
|
||||
- **Search URL**: `?search=zoekterm` voor bookmarkable searches
|
||||
### 🔍 Zoekfunctionaliteit
|
||||
- Volledige tekst zoek door alle content
|
||||
- Resultaten met snippets en highlighting
|
||||
- Directe navigatie naar gevonden pagina's
|
||||
- SEO-vriendelijke zoekresultaten
|
||||
- Search URL: `?search=zoekterm`
|
||||
|
||||
### 🧭 **Configuratie**
|
||||
- **JSON configuratie** in project root (`config.json`)
|
||||
- **Dynamische homepage** instelling of automatische detectie
|
||||
- **SEO instellingen** (description, keywords)
|
||||
- **Author informatie** met links naar website en Git
|
||||
- **Thema configuratie** met kleur instellingen
|
||||
- **Language settings** voor meertalige content
|
||||
- **Feature toggles** voor search, breadcrumbs, auto-linking
|
||||
### 🧭 Configuratie
|
||||
- **JSON configuratie** in `config.json`
|
||||
- Dynamische homepage instelling
|
||||
- SEO instellingen (description, keywords)
|
||||
- Author informatie met links
|
||||
- Thema configuratie met kleuren
|
||||
- Language settings
|
||||
- Feature toggles
|
||||
|
||||
### 🎨 **Layout & Design**
|
||||
- **Flexbox layout** voor moderne, responsive structuur
|
||||
- **Fixed header** met logo en zoekfunctie
|
||||
- **Breadcrumb navigatie** tussen header en content
|
||||
- **Fixed footer** met file info en links
|
||||
- **Bootstrap 5** styling en componenten
|
||||
- **Custom CSS** voor specifieke styling
|
||||
- **Mustache templates** met conditionals en partials
|
||||
- **Semantic HTML5** structuur voor SEO
|
||||
|
||||
### 📱 **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
|
||||
### 🎨 Layout & Design
|
||||
- Flexbox layout voor responsive structuur
|
||||
- Fixed header met logo en zoekfunctie
|
||||
- Breadcrumb navigatie
|
||||
- Fixed footer met file info en links
|
||||
- Bootstrap 5 styling
|
||||
- Mustache templates
|
||||
- Semantic HTML5 structuur
|
||||
- **Dynamic layouts** met YAML frontmatter
|
||||
- **Sidebar support** met plugin integratie
|
||||
|
||||
## Installatie
|
||||
|
||||
1. **Upload bestanden** naar webserver
|
||||
2. **Stel permissies in** voor webserver
|
||||
3. **Configureer** `config.json` indien nodig
|
||||
4. **Toegang** tot website via browser
|
||||
1. Upload bestanden naar webserver
|
||||
2. Stel permissies in voor webserver
|
||||
3. Configureer `config.json` indien nodig
|
||||
4. Toegang tot website via browser
|
||||
|
||||
## Configuratie
|
||||
|
||||
### Basis Configuratie (`config.json`)
|
||||
|
||||
```json
|
||||
\`\`\`json
|
||||
{
|
||||
"site_title": "CodePress",
|
||||
"content_dir": "content",
|
||||
"templates_dir": "engine/templates",
|
||||
"default_page": "auto",
|
||||
"homepage": "welkom",
|
||||
"language": {
|
||||
"default": "nl",
|
||||
"available": ["nl", "en"]
|
||||
@ -88,12 +74,13 @@ CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd
|
||||
"header_color": "#0a369d",
|
||||
"header_font_color": "#ffffff",
|
||||
"navigation_color": "#2754b4",
|
||||
"navigation_font_color": "#ffffff"
|
||||
"navigation_font_color": "#ffffff",
|
||||
"sidebar_background": "#f8f9fa",
|
||||
"sidebar_border": "#dee2e6"
|
||||
},
|
||||
"author": {
|
||||
"name": "Edwin Noorlander",
|
||||
"website": "https://noorlander.info",
|
||||
"git": "https://git.noorlander.info/E.Noorlander/CodePress.git"
|
||||
"name": "E. Noorlander",
|
||||
"website": "https://noorlander.info"
|
||||
},
|
||||
"seo": {
|
||||
"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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
### Bestandsstructuur
|
||||
|
||||
```
|
||||
\`\`\`
|
||||
content/
|
||||
├── map1/
|
||||
│ ├── submap1/
|
||||
@ -136,74 +109,47 @@ content/
|
||||
│ └── pagina4.md
|
||||
├── homepage.md
|
||||
└── index.html
|
||||
```
|
||||
\`\`\`
|
||||
|
||||
### Bestandsnamen
|
||||
|
||||
- **Gebruik lowercase** bestandsnamen
|
||||
- **Geen spaties** - gebruik `-` of `_` als scheidingsteken
|
||||
- **Logische extensies** - `.md`, `.php`, `.html`
|
||||
- **Unieke namen** - geen duplicaten binnen dezelfde map
|
||||
- **Language prefixes** - `nl.bestand.md` en `en.bestand.md` voor meertalige content
|
||||
- **Display names** - `bestands-naam` wordt automatisch "Bestands Naam" in menu's
|
||||
- **Special cases** - `phpinfo` → "phpinfo", `ict` → "ICT"
|
||||
- Gebruik lowercase bestandsnamen
|
||||
- Geen spaties - gebruik `-` of `_`
|
||||
- Logische extensies - `.md`, `.php`, `.html`
|
||||
- Unieke namen - geen duplicaten
|
||||
- Language prefixes - `nl.bestand.md` en `en.bestand.md`
|
||||
|
||||
## 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
|
||||
|
||||
#### **Site Info**
|
||||
- **`{{site_title}}`** - Website titel
|
||||
- **`{{author_name}}`** - Auteur naam
|
||||
- **`{{author_website}}`** - Auteur website
|
||||
- **`{{author_git}}`** - Git repository link
|
||||
#### Site Info
|
||||
- `site_title` - Website titel
|
||||
- `author_name` - Auteur naam
|
||||
- `author_website` - Auteur website
|
||||
- `author_git` - Git repository link
|
||||
|
||||
#### **Page Info**
|
||||
- **`{{page_title}}`** - Pagina titel (bestandsnaam zonder extensie)
|
||||
- **`{{content}}`** - Content (HTML)
|
||||
- **`{{file_info}}`** - Bestandsinformatie (datum, grootte)
|
||||
- **`{{is_homepage}}`** - Boolean: is dit de homepage?
|
||||
#### Page Info
|
||||
- `page_title` - Pagina titel
|
||||
- `content` - Content (HTML)
|
||||
- `file_info` - Bestandsinformatie
|
||||
- `is_homepage` - Boolean: is dit de homepage?
|
||||
|
||||
#### **Navigation**
|
||||
- **`{{menu}}`** - Navigatie menu
|
||||
- **`{{breadcrumb}}`** - Breadcrumb navigatie
|
||||
- **`{{homepage}}`** - Homepage link
|
||||
- **`{{homepage_title}}`** - Homepage titel
|
||||
#### Navigation
|
||||
- `menu` - Navigatie menu
|
||||
- `breadcrumb` - Breadcrumb navigatie
|
||||
- `homepage` - Homepage link
|
||||
|
||||
#### **Theme**
|
||||
- **`{{header_color}}`** - Header achtergrondkleur
|
||||
- **`{{header_font_color}}`** - Header tekstkleur
|
||||
- **`{{navigation_color}}`** - Navigatie achtergrondkleur
|
||||
- **`{{navigation_font_color}}`** - Navigatie tekstkleur
|
||||
#### Theme
|
||||
- `header_color` - Header achtergrondkleur
|
||||
- `header_font_color` - Header tekstkleur
|
||||
- `navigation_color` - Navigatie achtergrondkleur
|
||||
- `navigation_font_color` - Navigatie tekstkleur
|
||||
|
||||
#### **Language**
|
||||
- **`{{current_lang}}`** - Huidige taal (nl/en)
|
||||
- **`{{current_lang_upper}}`** - Huidige taal (NL/EN)
|
||||
- **`{{available_langs}}`** - Beschikbare talen
|
||||
- **`{{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?
|
||||
#### Language
|
||||
- `current_lang` - Huidige taal (nl/en)
|
||||
- `current_lang_upper` - Huidige taal (NL/EN)
|
||||
- `t_*` - Vertaalde strings
|
||||
|
||||
## URL Structuur
|
||||
|
||||
@ -214,99 +160,121 @@ engine/templates/
|
||||
- **Handleiding**: `?guide`
|
||||
- **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
|
||||
|
||||
### Meta Tags
|
||||
|
||||
De CMS voegt automatisch de volgende meta tags toe:
|
||||
|
||||
```html
|
||||
De CMS voegt automatisch meta tags toe:
|
||||
\`\`\`html
|
||||
<meta name="generator" content="CodePress CMS">
|
||||
<meta name="application-name" content="CodePress">
|
||||
<meta name="author" content="Edwin Noorlander">
|
||||
<meta name="author" content="E. Noorlander">
|
||||
<meta name="description" 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
|
||||
- **Geen dubbele links** voor dezelfde pagina
|
||||
- **SEO-vriendelijke** URL structuur
|
||||
\`\`\`
|
||||
plugins/
|
||||
├── README.md
|
||||
├── 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
|
||||
|
||||
### Hoe stel ik de homepage in?
|
||||
|
||||
1. **Automatisch**: Laat de CMS het eerste bestand kiezen
|
||||
2. **Handmatig**: Stel `"homepage": "pagina-naam"` in `config.json`
|
||||
3. **Flexibel**: Werkt met elk bestandstype (md, php, html)
|
||||
2. **Handmatig**: Stel `"default_page": "pagina-naam"` in `config.json`
|
||||
|
||||
### Hoe werkt de navigatie?
|
||||
|
||||
- **Mappen** worden dropdown menus
|
||||
- **Bestanden** worden directe links
|
||||
- **Sub-mappen** worden geneste dropdowns
|
||||
- **Home knop** linkt altijd naar de homepage
|
||||
|
||||
### Hoe voeg ik nieuwe content toe?
|
||||
|
||||
1. **Upload** bestanden naar de `content/` map
|
||||
2. **Organiseer** in logische mappen
|
||||
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
|
||||
1. Upload bestanden naar de `content/` map
|
||||
2. Organiseer in logische mappen
|
||||
3. Gebruik juiste bestandsnamen en extensies
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pagina niet gevonden (404)
|
||||
|
||||
1. **Controleer** bestandsnaam en pad
|
||||
2. **Controleer** bestandsextensie (.md, .php, .html)
|
||||
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
|
||||
1. Controleer bestandsnaam en pad
|
||||
2. Controleer bestandsextensie (.md, .php, .html)
|
||||
3. Controleer permissies van bestanden
|
||||
|
||||
### Navigatie niet bijgewerkt
|
||||
|
||||
1. **Herlaad** de pagina
|
||||
2. **Controleer** content map structuur
|
||||
3. **Controleer** bestandsnamen (geen spaties)
|
||||
4. **Controleer** PHP cache indien aanwezig
|
||||
1. Herlaad de pagina
|
||||
2. Controleer content map structuur
|
||||
3. Controleer bestandsnamen (geen spaties)
|
||||
|
||||
## Ondersteuning
|
||||
|
||||
@ -317,4 +285,4 @@ Voor technische ondersteuning:
|
||||
|
||||
## Licentie
|
||||
|
||||
CodePress CMS is open-source software. Controleer de licentie in de repository voor meer informatie.
|
||||
CodePress CMS is open-source software.
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codepress",
|
||||
"version": "1.0.0",
|
||||
"version": "1.5.0",
|
||||
"description": "A lightweight, file-based Content Management System built with PHP and Bootstrap.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
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
|
||||
20
version.php
20
version.php
@ -6,12 +6,26 @@
|
||||
*/
|
||||
|
||||
return [
|
||||
'version' => '1.0.0',
|
||||
'release_date' => '2025-11-24',
|
||||
'codename' => 'Stable',
|
||||
'version' => '1.5.0',
|
||||
'release_date' => '2025-11-26',
|
||||
'codename' => 'Enhanced',
|
||||
'status' => 'stable',
|
||||
|
||||
'changelog' => [
|
||||
'1.5.0' => [
|
||||
'date' => '2025-11-26',
|
||||
'changes' => [
|
||||
'Fix critical guide template variable replacement bug',
|
||||
'Complete guide documentation rewrite with comprehensive examples',
|
||||
'Implement plugin system with HTMLBlock and MQTTTracker plugins',
|
||||
'Enhanced bilingual support (NL/EN) throughout the system',
|
||||
'Improved template system with better layout options',
|
||||
'Enhanced security headers and code quality improvements',
|
||||
'Updated documentation and configuration examples',
|
||||
'Plugin architecture for extensibility',
|
||||
'Real-time analytics and tracking capabilities',
|
||||
]
|
||||
],
|
||||
'1.0.0' => [
|
||||
'date' => '2025-11-24',
|
||||
'changes' => [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user