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