Remove language-specific content handling

Refactor to use plugin system for language support

Remove hardcoded language features from core

Move language handling to plugin system

Improve content file discovery

Simplify context creation

Add plugin system documentation

Implement hook system for extensibility

Add template variable hook

Add context storage for plugins

Improve error handling

Refactor rendering logic

Improve list view sorting

Add support for custom list templates

Improve metadata handling

Add plugin system reference documentation
This commit is contained in:
Ruben 2025-11-25 20:19:12 +01:00
parent 24ee209e17
commit a205f2cbd7
8 changed files with 524 additions and 315 deletions

View file

@ -2,6 +2,7 @@
// Load modular components
require_once __DIR__ . '/constants.php';
require_once __DIR__ . '/hooks.php';
require_once __DIR__ . '/context.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/plugins.php';
@ -9,9 +10,12 @@ require_once __DIR__ . '/config.php';
require_once __DIR__ . '/content.php';
require_once __DIR__ . '/rendering.php';
// Create context - no more globals!
// Create context
$ctx = createContext();
// Store globally for easy access
$GLOBALS['ctx'] = $ctx;
// Check for assets in /custom/assets/ served at root level
$assetPath = dirname(__DIR__) . '/custom/assets/' . $ctx->requestPath;
if (file_exists($assetPath) && is_file($assetPath)) {
@ -22,7 +26,7 @@ if (file_exists($assetPath) && is_file($assetPath)) {
// Handle frontpage
if (empty($ctx->requestPath)) {
$contentFiles = findAllContentFiles($ctx->contentDir, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs);
$contentFiles = findAllContentFiles($ctx->contentDir);
if (!empty($contentFiles)) {
renderMultipleFiles($ctx, $contentFiles, $ctx->contentDir);
}
@ -33,50 +37,43 @@ $parsedPath = parseRequestPath($ctx);
switch ($parsedPath['type']) {
case 'page':
// Page-type folder with content files (no subdirectories)
// Redirect to add trailing slash if needed
if (!empty($parsedPath['needsSlash'])) {
header('Location: ' . rtrim($_SERVER['REQUEST_URI'], '/') . '/', true, 301);
exit;
}
renderMultipleFiles($ctx, $parsedPath['files'], $parsedPath['path']);
case 'file':
// Direct file access or legacy single file
// Redirect to add trailing slash if this is a directory-based page
if (!empty($parsedPath['needsSlash'])) {
header('Location: ' . rtrim($_SERVER['REQUEST_URI'], '/') . '/', true, 301);
exit;
}
renderFile($ctx, $parsedPath['path']);
case 'directory':
$dir = $parsedPath['path'];
if (file_exists("$dir/index.php")) {
renderFile($ctx, "$dir/index.php");
// Redirect to add trailing slash if needed
if (!$ctx->hasTrailingSlash) {
header('Location: ' . rtrim($_SERVER['REQUEST_URI'], '/') . '/', true, 301);
exit;
}
$contentFiles = findAllContentFiles($dir);
if (!empty($contentFiles)) {
renderMultipleFiles($ctx, $contentFiles, $dir);
}
break;
case 'list':
$dir = $parsedPath['path'];
// Check for page content files in this directory
$pageContent = null;
$contentFiles = findAllContentFiles($dir, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs);
$contentFiles = findAllContentFiles($dir);
if (!empty($contentFiles)) {
$pageContent = implode('', array_map('renderContentFile', $contentFiles));
}
// Load metadata for this directory
$metadata = loadMetadata($dir, $ctx->currentLang, $ctx->defaultLang);
$metadata = loadMetadata($dir);
// Select list template based on metadata page_template
$listTemplate = $ctx->templates->list;
if (isset($metadata['page_template']) && !empty($metadata['page_template'])) {
$templateName = $metadata['page_template'];
// Add .php extension if not present
if (!str_ends_with($templateName, '.php')) {
$templateName = str_replace('.php', '', $templateName);
$templateName .= '';
}
$customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php";
$defaultTemplate = __DIR__ . "/default/templates/$templateName.php";
if (file_exists($customTemplate)) {
$listTemplate = $customTemplate;
} elseif (file_exists($defaultTemplate)) {
@ -89,73 +86,70 @@ switch ($parsedPath['type']) {
$items = array_filter(array_map(function($item) use ($dir, $ctx) {
$itemPath = "$dir/$item";
// Check if content exists for current language
if ($ctx->currentLang !== $ctx->defaultLang && shouldHideUntranslated()) {
$hasLangContent = hasLanguageContent($itemPath, $ctx->currentLang, CONTENT_EXTENSIONS);
$hasLangMetadata = hasLanguageMetadata($itemPath, $ctx->currentLang);
if (!$hasLangContent && !$hasLangMetadata) return null;
}
$metadata = loadMetadata($itemPath, $ctx->currentLang, $ctx->defaultLang);
$metadata = loadMetadata($itemPath);
$coverImage = findCoverImage($itemPath);
$pdfFile = findPdfFile($itemPath);
$title = $metadata['title'] ?? extractTitle($itemPath, $ctx->currentLang, $ctx->defaultLang) ?? $item;
$title = $metadata['title'] ?? extractTitle($itemPath) ?? $item;
$date = null;
if (isset($metadata['date'])) {
$date = formatDate($metadata['date'], $ctx->currentLang);
$date = $metadata['date'];
// Let plugins format date
$date = Hooks::apply(Hook::PROCESS_CONTENT, $date, 'date_format');
} else {
$date = extractDateFromFolder($item, $ctx->currentLang) ?: date("F d, Y", filemtime($itemPath));
$date = extractDateFromFolder($item) ?: date("F d, Y", filemtime($itemPath));
}
// Use translated slug if available, otherwise use folder name
$urlSlug = ($ctx->currentLang !== $ctx->defaultLang && $metadata && isset($metadata['slug']))
? $metadata['slug']
: $item;
// Use slug if available
$urlSlug = ($metadata && isset($metadata['slug'])) ? $metadata['slug'] : $item;
$baseUrl = $ctx->langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($urlSlug);
$baseUrl = '/' . trim($ctx->requestPath, '/') . '/' . urlencode($urlSlug);
return [
'title' => $title,
'url' => $baseUrl . '/',
'date' => $date,
'url' => $baseUrl,
'cover' => $coverImage ? "$baseUrl/$coverImage" : null,
'summary' => $metadata['summary'] ?? null,
'cover' => $coverImage ? "$baseUrl/$coverImage" : null,
'pdf' => $pdfFile ? "$baseUrl/$pdfFile" : null,
'redirect' => $metadata['redirect'] ?? null
];
}, $subdirs));
ob_start();
include $listTemplate;
$content = ob_get_clean();
// Sort by date (newest first) if dates are present
usort($items, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
// Prepare all variables for base template
$currentLang = $ctx->currentLang;
$navigation = $ctx->navigation;
$homeLabel = $ctx->homeLabel;
$translations = $ctx->translations;
$pageTitle = $metadata['title'] ?? null;
$metaDescription = extractMetaDescription($dir, $metadata, $ctx->currentLang, $ctx->defaultLang);
$metaDescription = extractMetaDescription($dir, $metadata);
// Check for page-specific CSS
$pageCss = findPageCss($dir, $ctx->contentDir);
$pageCssUrl = $pageCss['url'] ?? null;
$pageCssHash = $pageCss['hash'] ?? null;
// Check for cover image for social media
$coverImage = findCoverImage($dir);
$socialImageUrl = null;
if ($coverImage) {
$relativePath = str_replace($ctx->contentDir, '', $dir);
$relativePath = trim($relativePath, '/');
$socialImageUrl = '/' . ($relativePath ? $relativePath . '/' : '') . $coverImage;
}
// Let plugins add template variables
$templateVars = Hooks::apply(Hook::TEMPLATE_VARS, [
'navigation' => $navigation,
'homeLabel' => $homeLabel,
'pageTitle' => $pageTitle,
'metaDescription' => $metaDescription,
'pageCss' => $pageCss,
'items' => $items,
'pageContent' => $pageContent
], $ctx);
extract($templateVars);
ob_start();
require $listTemplate;
$content = ob_get_clean();
include $ctx->templates->base;
exit;
renderTemplate($ctx, $content);
break;
case 'not_found':
renderTemplate($ctx, "<h1>404 Not Found</h1><p>The requested resource was not found.</p>", 404);
http_response_code(404);
renderTemplate($ctx, "<h1>404 - Page Not Found</h1><p>The requested page could not be found.</p>", 404);
break;
}