Merge branch 'development'
This commit is contained in:
commit
0ef42c120e
14
config.json
14
config.json
@ -1,12 +1,18 @@
|
||||
{
|
||||
"site_title": "CodePress",
|
||||
"content_dir": "public/content",
|
||||
"content_dir": "content",
|
||||
"templates_dir": "engine/templates",
|
||||
"default_page": "welkom",
|
||||
|
||||
"theme": {
|
||||
"primary_color": "#0d6efd",
|
||||
"navbar_style": "bg-primary"
|
||||
"header_color": "#0a369d",
|
||||
"header_font_color": "#ffffff",
|
||||
"navigation_color": "#2754b4",
|
||||
"navigation_font_color": "#ffffff"
|
||||
},
|
||||
"language": {
|
||||
"default": "nl",
|
||||
"available": ["nl", "en"]
|
||||
},
|
||||
"seo": {
|
||||
"description": "CodePress CMS - Lightweight file-based content management system",
|
||||
@ -22,4 +28,4 @@
|
||||
"search_enabled": true,
|
||||
"breadcrumbs_enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,8 @@ class CodePressCMS {
|
||||
private $config;
|
||||
private $menu = [];
|
||||
private $searchResults = [];
|
||||
private $currentLanguage;
|
||||
private $translations = [];
|
||||
|
||||
/**
|
||||
* Constructor - Initialize the CMS with configuration
|
||||
@ -32,6 +34,8 @@ class CodePressCMS {
|
||||
*/
|
||||
public function __construct($config) {
|
||||
$this->config = $config;
|
||||
$this->currentLanguage = $this->getCurrentLanguage();
|
||||
$this->translations = $this->loadTranslations($this->currentLanguage);
|
||||
$this->buildMenu();
|
||||
|
||||
if (isset($_GET['search'])) {
|
||||
@ -39,6 +43,46 @@ class CodePressCMS {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current language from URL or use default
|
||||
*
|
||||
* @return string Current language code
|
||||
*/
|
||||
private function getCurrentLanguage() {
|
||||
return $_GET['lang'] ?? $this->config['language']['default'] ?? 'nl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Load translations for specified language
|
||||
*
|
||||
* @param string $lang Language code
|
||||
* @return array Translations array
|
||||
*/
|
||||
private function loadTranslations($lang) {
|
||||
$langFile = __DIR__ . '/../../lang/' . $lang . '.php';
|
||||
error_log("Loading language file: " . $langFile);
|
||||
if (file_exists($langFile)) {
|
||||
$translations = include $langFile;
|
||||
error_log("Loaded translations for " . $lang . ": " . print_r($translations, true));
|
||||
return $translations;
|
||||
}
|
||||
// Fallback to default language
|
||||
$defaultLang = $this->config['language']['default'] ?? 'nl';
|
||||
$defaultLangFile = __DIR__ . '/../../lang/' . $defaultLang . '.php';
|
||||
error_log("Fallback to default language: " . $defaultLangFile);
|
||||
return file_exists($defaultLangFile) ? include $defaultLangFile : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translated text
|
||||
*
|
||||
* @param string $key Translation key
|
||||
* @return string Translated text
|
||||
*/
|
||||
public function t($key) {
|
||||
return $this->translations[$key] ?? $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build menu structure from content directory
|
||||
*
|
||||
@ -263,12 +307,12 @@ class CodePressCMS {
|
||||
*/
|
||||
private function getSearchResults() {
|
||||
$query = $_GET['search'];
|
||||
$content = '<h2>Search Results for: "' . htmlspecialchars($query) . '"</h2>';
|
||||
$content = '<h2>' . $this->t('search') . ' ' . $this->t('results_found') . ': "' . htmlspecialchars($query) . '"</h2>';
|
||||
|
||||
if (empty($this->searchResults)) {
|
||||
$content .= '<p>No results found.</p>';
|
||||
$content .= '<p>' . $this->t('no_results') . '.</p>';
|
||||
} else {
|
||||
$content .= '<p>Found ' . count($this->searchResults) . ' results:</p>';
|
||||
$content .= '<p>' . count($this->searchResults) . ' ' . $this->t('results_found') . ':</p>';
|
||||
foreach ($this->searchResults as $result) {
|
||||
$content .= '<div class="card mb-3">';
|
||||
$content .= '<div class="card-body">';
|
||||
@ -593,7 +637,7 @@ class CodePressCMS {
|
||||
$result = $this->parseMarkdown($content);
|
||||
|
||||
// Set special title for guide
|
||||
$result['title'] = 'Handleiding - CodePress CMS';
|
||||
$result['title'] = $this->t('manual') . ' - CodePress CMS';
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -647,11 +691,8 @@ class CodePressCMS {
|
||||
sort($items);
|
||||
$hasContent = false;
|
||||
|
||||
$content .= '<div class="row">';
|
||||
|
||||
// Subdirectories
|
||||
$subdirs = [];
|
||||
$files = [];
|
||||
// Collect all items
|
||||
$allItems = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item[0] === '.') continue;
|
||||
@ -660,57 +701,46 @@ class CodePressCMS {
|
||||
$relativePath = $pagePath ? $pagePath . '/' . $item : $item;
|
||||
|
||||
if (is_dir($itemPath)) {
|
||||
$subdirs[] = [
|
||||
$allItems[] = [
|
||||
'name' => ucfirst($item),
|
||||
'path' => $relativePath,
|
||||
'url' => '?page=' . $relativePath
|
||||
'url' => '?page=' . $relativePath,
|
||||
'icon' => 'bi-folder',
|
||||
'type' => 'directory'
|
||||
];
|
||||
} elseif (preg_match('/\.(md|php|html)$/', $item)) {
|
||||
$extractedTitle = $this->extractPageTitle($itemPath);
|
||||
$fileTitle = $extractedTitle ?: ucfirst(pathinfo($item, PATHINFO_FILENAME));
|
||||
$pathWithoutExt = preg_replace('/\.[^.]+$/', '', $relativePath);
|
||||
$files[] = [
|
||||
$icon = pathinfo($item, PATHINFO_EXTENSION) === 'md' ? 'bi-file-text' :
|
||||
(pathinfo($item, PATHINFO_EXTENSION) === 'php' ? 'bi-file-code' : 'bi-file-earmark');
|
||||
$allItems[] = [
|
||||
'name' => $fileTitle,
|
||||
'path' => $pathWithoutExt,
|
||||
'url' => '?page=' . $pathWithoutExt,
|
||||
'type' => pathinfo($item, PATHINFO_EXTENSION)
|
||||
'icon' => $icon,
|
||||
'type' => 'file'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Display subdirectories
|
||||
if (!empty($subdirs)) {
|
||||
$content .= '<div class="col-md-6">';
|
||||
$content .= '<h3>📁 Mappen</h3>';
|
||||
// Display all items in a single column
|
||||
if (!empty($allItems)) {
|
||||
$content .= '<div class="list-group">';
|
||||
foreach ($subdirs as $subdir) {
|
||||
$content .= '<a href="' . htmlspecialchars($subdir['url']) . '" class="list-group-item list-group-item-action">';
|
||||
$content .= '<i class="bi bi-folder"></i> ' . htmlspecialchars($subdir['name']);
|
||||
foreach ($allItems as $item) {
|
||||
$content .= '<a href="' . htmlspecialchars($item['url']) . '" class="list-group-item list-group-item-action d-flex align-items-center">';
|
||||
$content .= '<i class="bi ' . $item['icon'] . ' me-3"></i>';
|
||||
$content .= '<span>' . htmlspecialchars($item['name']) . '</span>';
|
||||
$content .= '</a>';
|
||||
}
|
||||
$content .= '</div></div>';
|
||||
$hasContent = true;
|
||||
}
|
||||
|
||||
// Display files
|
||||
if (!empty($files)) {
|
||||
$content .= '<div class="col-md-6">';
|
||||
$content .= '<h3>📄 Pagina\'s</h3>';
|
||||
$content .= '<div class="list-group">';
|
||||
foreach ($files as $file) {
|
||||
$icon = $file['type'] === 'md' ? 'bi-file-text' : ($file['type'] === 'php' ? 'bi-file-code' : 'bi-file-earmark');
|
||||
$content .= '<a href="' . htmlspecialchars($file['url']) . '" class="list-group-item list-group-item-action">';
|
||||
$content .= '<i class="bi ' . $icon . '"></i> ' . htmlspecialchars($file['name']);
|
||||
$content .= '</a>';
|
||||
}
|
||||
$content .= '</div></div>';
|
||||
$content .= '</div>';
|
||||
$hasContent = true;
|
||||
}
|
||||
|
||||
$content .= '</div>';
|
||||
|
||||
if (!$hasContent) {
|
||||
$content .= '<p>Deze map is leeg.</p>';
|
||||
$content .= '<p>' . $this->t('directory_empty') . '.</p>';
|
||||
}
|
||||
|
||||
return [
|
||||
@ -726,8 +756,8 @@ class CodePressCMS {
|
||||
*/
|
||||
private function getError404() {
|
||||
return [
|
||||
'title' => 'Page Not Found',
|
||||
'content' => '<h1>404 - Page Not Found</h1><p>The page you are looking for does not exist.</p>'
|
||||
'title' => $this->t('page_not_found'),
|
||||
'content' => '<h1>404 - ' . $this->t('page_not_found') . '</h1><p>' . $this->t('page_not_found_text') . '</p>'
|
||||
];
|
||||
}
|
||||
|
||||
@ -748,7 +778,7 @@ class CodePressCMS {
|
||||
public function render() {
|
||||
$page = $this->getPage();
|
||||
$menu = $this->getMenu();
|
||||
$breadcrumb = $this->getBreadcrumb();
|
||||
$breadcrumb = $this->generateBreadcrumb();
|
||||
|
||||
// Get homepage title
|
||||
$homepageTitle = $this->getHomepageTitle();
|
||||
@ -764,30 +794,57 @@ class CodePressCMS {
|
||||
'default_page' => $this->config['default_page'],
|
||||
'homepage' => $this->config['default_page'],
|
||||
'homepage_title' => $homepageTitle,
|
||||
'is_homepage' => (!isset($_GET['page']) || $_GET['page'] === $this->config['default_page']),
|
||||
'home_active_class' => (!isset($_GET['page']) || $_GET['page'] === $this->config['default_page']) ? 'active' : '',
|
||||
'author_name' => $this->config['author']['name'] ?? 'CodePress Developer',
|
||||
'author_website' => $this->config['author']['website'] ?? '#',
|
||||
'author_git' => $this->config['author']['git'] ?? '#',
|
||||
'seo_description' => $this->config['seo']['description'] ?? 'CodePress CMS - Lightweight file-based content management system',
|
||||
'seo_keywords' => $this->config['seo']['keywords'] ?? 'cms, php, content management, file-based'
|
||||
'seo_keywords' => $this->config['seo']['keywords'] ?? 'cms, php, content management, file-based',
|
||||
// Theme colors
|
||||
'header_color' => $this->config['theme']['header_color'] ?? '#0d6efd',
|
||||
'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',
|
||||
// Language
|
||||
'current_lang' => $this->currentLanguage,
|
||||
'current_lang_upper' => strtoupper($this->currentLanguage),
|
||||
'available_langs' => $this->config['language']['available'] ?? ['nl', 'en'],
|
||||
// Translations
|
||||
't_home' => $this->t('home'),
|
||||
't_search' => $this->t('search'),
|
||||
't_search_placeholder' => $this->t('search_placeholder'),
|
||||
't_search_button' => $this->t('search_button'),
|
||||
't_welcome' => $this->t('welcome'),
|
||||
't_created' => $this->t('created'),
|
||||
't_modified' => $this->t('modified'),
|
||||
't_author' => $this->t('author'),
|
||||
't_manual' => $this->t('manual'),
|
||||
't_no_content' => $this->t('no_content'),
|
||||
't_no_results' => $this->t('no_results'),
|
||||
't_results_found' => $this->t('results_found'),
|
||||
't_breadcrumb_home' => $this->t('breadcrumb_home'),
|
||||
't_file_details' => $this->t('file_details'),
|
||||
't_guide' => $this->t('guide'),
|
||||
't_powered_by' => $this->t('powered_by'),
|
||||
't_directory_empty' => $this->t('directory_empty'),
|
||||
'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')
|
||||
];
|
||||
|
||||
// File info for footer
|
||||
if (isset($page['file_info'])) {
|
||||
$templateData['file_info'] = 'Created: ' . htmlspecialchars($page['file_info']['created']) .
|
||||
' | Modified: ' . htmlspecialchars($page['file_info']['modified']);
|
||||
$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>';
|
||||
} else {
|
||||
$templateData['file_info'] = '';
|
||||
$templateData['file_info_block'] = '';
|
||||
}
|
||||
|
||||
// File info for footer
|
||||
if (isset($page['file_info'])) {
|
||||
$templateData['file_info'] = 'Created: ' . htmlspecialchars($page['file_info']['created']) .
|
||||
' | Modified: ' . htmlspecialchars($page['file_info']['modified']);
|
||||
} else {
|
||||
$templateData['file_info'] = '';
|
||||
}
|
||||
|
||||
|
||||
// Check if content exists for guide link
|
||||
$hasContent = !$this->isContentDirEmpty();
|
||||
@ -796,60 +853,35 @@ class CodePressCMS {
|
||||
// Don't show site title link on guide page
|
||||
$templateData['show_site_link'] = !$this->isContentDirEmpty() && !isset($_GET['guide']);
|
||||
|
||||
// Load partials manually
|
||||
$hasContent = !$this->isContentDirEmpty() && !isset($_GET['guide']);
|
||||
|
||||
$headerContent = file_get_contents($this->config['templates_dir'] . '/assets/header.mustache');
|
||||
if (!$hasContent) {
|
||||
// Remove the link from header when no content
|
||||
$headerContent = preg_replace('/<a href="[^"]*" class="site-title-link">\s*<h1[^>]*>(.*?)<\/h1>\s*<\/a>/', '<h1 class="h3 mb-0">$1</h1>', $headerContent);
|
||||
}
|
||||
|
||||
$footerContent = file_get_contents($this->config['templates_dir'] . '/assets/footer.mustache');
|
||||
if (!$hasContent) {
|
||||
// Remove guide link from footer when no content
|
||||
$footerContent = preg_replace('/<span class="file-details">\s*\|\s*<a href="\?guide"[^>]*>Handleiding<\/a><\/span>/', '', $footerContent);
|
||||
}
|
||||
// Load and render all templates with data
|
||||
$layoutTemplate = file_get_contents($this->config['templates_dir'] . '/layout.mustache');
|
||||
$headerTemplate = file_get_contents($this->config['templates_dir'] . '/assets/header.mustache');
|
||||
$navigationTemplate = file_get_contents($this->config['templates_dir'] . '/assets/navigation.mustache');
|
||||
$footerTemplate = file_get_contents($this->config['templates_dir'] . '/assets/footer.mustache');
|
||||
|
||||
// Determine content type and load appropriate template
|
||||
$contentType = $this->getContentType($page);
|
||||
$contentTemplateFile = $this->config['templates_dir'] . '/' . $contentType . '_content.mustache';
|
||||
$contentTemplate = file_exists($contentTemplateFile) ? file_get_contents($contentTemplateFile) : '<div class="content">{{{content}}}</div>';
|
||||
|
||||
// Determine content type and load appropriate template
|
||||
$pagePath = $_GET['page'] ?? $this->config['default_page'];
|
||||
$pagePath = preg_replace('/\.[^.]+$/', '', $pagePath);
|
||||
$filePath = $this->config['content_dir'] . '/' . $pagePath;
|
||||
|
||||
|
||||
$contentType = 'markdown'; // default
|
||||
if (file_exists($filePath . '.md')) {
|
||||
$contentType = 'markdown';
|
||||
} elseif (file_exists($filePath . '.php')) {
|
||||
$contentType = 'php';
|
||||
} elseif (file_exists($filePath . '.html')) {
|
||||
$contentType = 'html';
|
||||
}
|
||||
// Render all templates with data
|
||||
$renderedHeader = SimpleTemplate::render($headerTemplate, $templateData);
|
||||
$renderedNavigation = SimpleTemplate::render($navigationTemplate, $templateData);
|
||||
$renderedFooter = SimpleTemplate::render($footerTemplate, $templateData);
|
||||
$renderedContent = SimpleTemplate::render($contentTemplate, $templateData);
|
||||
|
||||
$contentTemplateFile = $this->config['templates_dir'] . '/' . $contentType . '_content.mustache';
|
||||
$contentTemplate = file_exists($contentTemplateFile) ? file_get_contents($contentTemplateFile) : '<div class="content">{{{content}}}</div>';
|
||||
// Replace partials in layout
|
||||
$finalTemplate = str_replace('{{>header}}', $renderedHeader, $layoutTemplate);
|
||||
$finalTemplate = str_replace('{{>navigation}}', $renderedNavigation, $finalTemplate);
|
||||
$finalTemplate = str_replace('{{>footer}}', $renderedFooter, $finalTemplate);
|
||||
$finalTemplate = str_replace('{{>content_template}}', $renderedContent, $finalTemplate);
|
||||
|
||||
$partials = [
|
||||
'header' => file_get_contents($this->config['templates_dir'] . '/assets/header.mustache'),
|
||||
'navigation' => file_get_contents($this->config['templates_dir'] . '/assets/navigation.mustache'),
|
||||
'footer' => file_get_contents($this->config['templates_dir'] . '/assets/footer.mustache'),
|
||||
'content_template' => $contentTemplate
|
||||
];
|
||||
// Render the final layout with all template data
|
||||
$renderedLayout = SimpleTemplate::render($finalTemplate, $templateData);
|
||||
|
||||
// Replace partials in template
|
||||
$template = file_get_contents($this->config['templates_dir'] . '/layout.mustache');
|
||||
$template = str_replace('{{>header}}', $partials['header'], $template);
|
||||
$template = str_replace('{{>navigation}}', $partials['navigation'], $template);
|
||||
$template = str_replace('{{>footer}}', $partials['footer'], $template);
|
||||
$template = str_replace('{{>content_template}}', $partials['content_template'], $template);
|
||||
|
||||
// Render template with data
|
||||
$renderedTemplate = SimpleTemplate::render($template, $templateData);
|
||||
echo $renderedTemplate;
|
||||
echo $renderedLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -857,30 +889,37 @@ class CodePressCMS {
|
||||
*
|
||||
* @return string Breadcrumb HTML
|
||||
*/
|
||||
private function getBreadcrumb() {
|
||||
private 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'] . '">Home</a></li><li class="breadcrumb-item active">Search</li></ol></nav>';
|
||||
return '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="?page=' . $this->config['default_page'] . '">></a></li><li class="breadcrumb-item"> > </li><li class="breadcrumb-item active">' . $this->t('search') . '</li></ol></nav>';
|
||||
}
|
||||
|
||||
$page = $_GET['page'] ?? $this->config['default_page'];
|
||||
$page = preg_replace('/\.[^.]+$/', '', $page);
|
||||
|
||||
if ($page === $this->config['default_page']) {
|
||||
return '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item active">Home</li></ol></nav>';
|
||||
return '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item active"><i class="bi bi-house"></i></li></ol></nav>';
|
||||
}
|
||||
|
||||
$parts = explode('/', $page);
|
||||
$breadcrumb = '<nav aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="?page=' . $this->config['default_page'] . '">Home</a></li>';
|
||||
$breadcrumb = '<nav aria-label="breadcrumb"><ol class="breadcrumb">';
|
||||
|
||||
// Start with home icon linking to default page (root)
|
||||
$breadcrumb .= '<li class="breadcrumb-item"><a href="?page=' . $this->config['default_page'] . '"><i class="bi bi-house"></i></a></li>';
|
||||
|
||||
// Split page path and build breadcrumb items
|
||||
$parts = explode('/', $page);
|
||||
$currentPath = '';
|
||||
|
||||
$path = '';
|
||||
foreach ($parts as $i => $part) {
|
||||
$path .= ($path ? '/' : '') . $part;
|
||||
$currentPath .= ($currentPath ? '/' : '') . $part;
|
||||
$title = ucfirst($part);
|
||||
|
||||
if ($i === count($parts) - 1) {
|
||||
$breadcrumb .= '<li class="breadcrumb-item active">' . $title . '</li>';
|
||||
// Last part - active page
|
||||
$breadcrumb .= '<li class="breadcrumb-item"> > </li><li class="breadcrumb-item active">' . $title . '</li>';
|
||||
} else {
|
||||
$breadcrumb .= '<li class="breadcrumb-item"><a href="?page=' . $path . '">' . $title . '</a></li>';
|
||||
// Parent directory - clickable link with separator
|
||||
$breadcrumb .= '<li class="breadcrumb-item"> > </li><li class="breadcrumb-item"><a href="?page=' . $currentPath . '">' . $title . '</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,7 +948,7 @@ class CodePressCMS {
|
||||
// Root level folders
|
||||
$html .= '<li class="nav-item dropdown">';
|
||||
$html .= '<a class="nav-link dropdown-toggle" href="#" id="' . $folderId . '" role="button" data-bs-toggle="dropdown" aria-expanded="' . ($isExpanded ? 'true' : 'false') . '">';
|
||||
$html .= htmlspecialchars($item['title']);
|
||||
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-down"></i>';
|
||||
$html .= '</a>';
|
||||
$html .= '<ul class="dropdown-menu" aria-labelledby="' . $folderId . '">';
|
||||
$html .= $this->renderMenu($item['children'], $level + 1);
|
||||
@ -919,7 +958,7 @@ class CodePressCMS {
|
||||
// Nested folders in dropdown
|
||||
$html .= '<li class="dropdown-submenu">';
|
||||
$html .= '<a class="dropdown-item dropdown-toggle" href="#" id="' . $folderId . '">';
|
||||
$html .= htmlspecialchars($item['title']);
|
||||
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-down"></i>';
|
||||
$html .= '</a>';
|
||||
$html .= '<ul class="dropdown-menu" aria-labelledby="' . $folderId . '">';
|
||||
$html .= $this->renderMenu($item['children'], $level + 1);
|
||||
|
||||
26
engine/lang/en.php
Normal file
26
engine/lang/en.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
return [
|
||||
'site_title' => 'CodePress',
|
||||
'home' => 'Home',
|
||||
'search' => 'Search',
|
||||
'search_placeholder' => 'Search...',
|
||||
'search_button' => 'Search',
|
||||
'welcome' => 'Welcome',
|
||||
'created' => 'Created',
|
||||
'modified' => 'Modified',
|
||||
'author' => 'Author',
|
||||
'manual' => 'Manual',
|
||||
'no_content' => 'No content found',
|
||||
'no_results' => 'No results found',
|
||||
'results_found' => 'results found',
|
||||
'breadcrumb_home' => 'Home',
|
||||
'file_details' => 'File details',
|
||||
'guide' => 'Guide',
|
||||
'powered_by' => 'Powered by',
|
||||
't_powered_by' => 'Powered by',
|
||||
'directory_empty' => 'This directory is empty',
|
||||
'page_not_found' => 'Page Not Found',
|
||||
'page_not_found_text' => 'The page you are looking for does not exist.',
|
||||
'mappen' => 'Folders',
|
||||
'paginas' => 'Pages'
|
||||
];
|
||||
26
engine/lang/nl.php
Normal file
26
engine/lang/nl.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
return [
|
||||
'site_title' => 'CodePress',
|
||||
'home' => 'Home',
|
||||
'search' => 'Zoeken',
|
||||
'search_placeholder' => 'Zoeken...',
|
||||
'search_button' => 'Zoeken',
|
||||
'welcome' => 'Welkom',
|
||||
'created' => 'Aangemaakt',
|
||||
'modified' => 'Aangepast',
|
||||
'author' => 'Auteur',
|
||||
'manual' => 'Handleiding',
|
||||
'no_content' => 'Geen inhoud gevonden',
|
||||
'no_results' => 'Geen resultaten gevonden',
|
||||
'results_found' => 'resultaten gevonden',
|
||||
'breadcrumb_home' => 'Home',
|
||||
'file_details' => 'Bestandsdetails',
|
||||
'guide' => 'Handleiding',
|
||||
'powered_by' => 'Mogelijk gemaakt door',
|
||||
't_powered_by' => 'Mogelijk gemaakt door',
|
||||
'directory_empty' => 'Deze map is leeg',
|
||||
'page_not_found' => 'Pagina niet gevonden',
|
||||
'page_not_found_text' => 'De pagina die u zoekt bestaat niet.',
|
||||
'mappen' => 'Mappen',
|
||||
'paginas' => 'Pagina\'s'
|
||||
];
|
||||
@ -10,11 +10,11 @@
|
||||
</div>
|
||||
<div class="site-info">
|
||||
<small class="text-muted">
|
||||
<a href="?guide" class="guide-link" title="Handleiding">
|
||||
<a href="?guide" class="guide-link" title="{{t_guide}}">
|
||||
<i class="bi bi-book"></i>
|
||||
</a>
|
||||
<span class="ms-2">|</span>
|
||||
Powered by <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a>
|
||||
{{t_powered_by}} <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,18 +1,50 @@
|
||||
<header class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<header class="navbar navbar-expand-lg navbar-dark" style="background-color: var(--header-bg);">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="?page={{default_page}}">
|
||||
<img src="/assets/icon.svg" alt="CodePress Logo" width="32" height="32" class="me-2">
|
||||
{{site_title}}
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#searchBar" aria-controls="searchBar" aria-expanded="false" aria-label="Toggle search">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
<!-- Desktop search and language -->
|
||||
<div class="d-none d-lg-flex ms-auto align-items-center">
|
||||
<form class="d-flex me-3" method="GET" action="">
|
||||
<input class="form-control me-2" type="search" name="search" placeholder="{{t_search_placeholder}}" value="{{search_query}}">
|
||||
<button class="btn btn-outline-light" type="submit">{{t_search_button}}</button>
|
||||
</form>
|
||||
|
||||
<!-- Language switcher -->
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-light" type="button" data-bs-toggle="dropdown">
|
||||
{{current_lang_upper}} <i class="bi bi-chevron-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="?lang=nl&page={{homepage}}">NL</a></li>
|
||||
<li><a class="dropdown-item" href="?lang=en&page={{homepage}}">EN</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse" id="searchBar">
|
||||
<form class="d-flex ms-auto" method="GET" action="">
|
||||
<input class="form-control me-2" type="search" name="search" placeholder="Search..." value="{{search_query}}">
|
||||
<button class="btn btn-outline-light" type="submit">Search</button>
|
||||
<!-- Mobile search and language toggle -->
|
||||
<div class="d-lg-none">
|
||||
<button class="btn btn-outline-light" type="button" data-bs-toggle="collapse" data-bs-target="#mobileSearch" aria-controls="mobileSearch" aria-expanded="false" aria-label="Toggle search">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-light" type="button" data-bs-toggle="dropdown">
|
||||
{{current_lang_upper}} <i class="bi bi-chevron-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="?lang=nl&page={{homepage}}">NL</a></li>
|
||||
<li><a class="dropdown-item" href="?lang=en&page={{homepage}}">EN</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile search bar -->
|
||||
<div class="collapse navbar-collapse d-lg-none" id="mobileSearch">
|
||||
<div class="container-fluid px-0">
|
||||
<form class="d-flex px-3 pb-3" method="GET" action="">
|
||||
<input class="form-control me-2" type="search" name="search" placeholder="{{t_search_placeholder}}" value="{{search_query}}">
|
||||
<button class="btn btn-outline-light" type="submit">{{t_search_button}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<nav class="navigation-section border-bottom navigation-50-opacity">
|
||||
<nav class="navigation-section">
|
||||
<div class="container-fluid">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<ul class="nav nav-tabs flex-wrap">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?page={{homepage}}">
|
||||
<a class="nav-link {{home_active_class}}" href="?page={{homepage}}">
|
||||
<i class="bi bi-house"></i> {{homepage_title}}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -25,6 +25,133 @@
|
||||
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/css/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="/assets/css/style.css" rel="stylesheet">
|
||||
<link href="/assets/css/mobile.css" rel="stylesheet">
|
||||
|
||||
<!-- Dynamic theme colors -->
|
||||
<style>
|
||||
:root {
|
||||
--header-bg: {{header_color}};
|
||||
--header-font: {{header_font_color}};
|
||||
--nav-bg: {{navigation_color}};
|
||||
--nav-font: {{navigation_font_color}};
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
.navbar {
|
||||
background-color: var(--header-bg) !important;
|
||||
}
|
||||
|
||||
.navbar .navbar-brand,
|
||||
.navbar .navbar-text,
|
||||
.navbar .form-control,
|
||||
.navbar .btn {
|
||||
color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
.navbar .form-control::placeholder {
|
||||
color: rgba(255,255,255,0.7) !important;
|
||||
}
|
||||
|
||||
.navbar .btn-outline-light {
|
||||
border-color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
/* Language dropdown styling */
|
||||
.dropdown-menu {
|
||||
background-color: var(--header-bg) !important;
|
||||
border: 1px solid var(--header-font) !important;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: rgba(255,255,255,0.1) !important;
|
||||
color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
/* Hide Bootstrap dropdown arrow and use custom icon */
|
||||
.dropdown-toggle::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.btn-outline-light {
|
||||
color: var(--header-font) !important;
|
||||
border-color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
.btn-outline-light:hover {
|
||||
background-color: rgba(255,255,255,0.1) !important;
|
||||
color: var(--header-font) !important;
|
||||
}
|
||||
|
||||
/* Fix button color when dropdown is open */
|
||||
.btn-outline-light:focus,
|
||||
.btn-outline-light:active,
|
||||
.show > .btn-outline-light.dropdown-toggle {
|
||||
background-color: rgba(255,255,255,0.1) !important;
|
||||
color: var(--header-font) !important;
|
||||
border-color: var(--header-font) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.bi-chevron-down {
|
||||
font-size: 0.75em;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
/* Remove Bootstrap default breadcrumb separators */
|
||||
.breadcrumb-item + .breadcrumb-item::before {
|
||||
content: "" !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* Custom breadcrumb styling */
|
||||
.breadcrumb {
|
||||
--bs-breadcrumb-divider: "";
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
color: var(--nav-font) !important;
|
||||
}
|
||||
|
||||
.breadcrumb-item a {
|
||||
color: var(--nav-font) !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.breadcrumb-item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Navigation section background */
|
||||
.navigation-section {
|
||||
background-color: var(--nav-bg) !important;
|
||||
color: var(--nav-font) !important;
|
||||
}
|
||||
|
||||
/* Remove nav-tabs background so it inherits from parent */
|
||||
.nav-tabs {
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
color: var(--nav-font) !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1) !important;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
background-color: rgba(255,255,255,0.2) !important;
|
||||
border-bottom: 2px solid var(--nav-font) !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{>header}}
|
||||
|
||||
54
public/assets/css/mobile.css
Normal file
54
public/assets/css/mobile.css
Normal file
@ -0,0 +1,54 @@
|
||||
/* Mobile search improvements */
|
||||
@media (max-width: 991.98px) {
|
||||
.navbar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Hide desktop elements on mobile */
|
||||
.d-none.d-lg-flex {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#mobileSearch {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--header-bg);
|
||||
z-index: 1030;
|
||||
}
|
||||
|
||||
#mobileSearch.show {
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Ensure navigation doesn't overlap with expanded search */
|
||||
.navigation-section {
|
||||
margin-top: 0;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop improvements */
|
||||
@media (min-width: 992px) {
|
||||
/* Hide mobile elements on desktop */
|
||||
.d-lg-none {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide mobile search on desktop */
|
||||
#mobileSearch {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search form improvements */
|
||||
.form-control[type="search"] {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.form-control[type="search"] {
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user