From 24ee209e170f2b6095712fcdfd010e75affc6f0a Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 11 Nov 2025 23:36:53 +0100 Subject: [PATCH] Add plugin system and improve language handling Add global and page-level plugin support Implement language-aware content filtering Add month translations to language files Refactor date formatting to use translations Move translation loading to plugin system Improve content availability checks --- app/config.php | 3 ++ app/content.php | 18 ++----- app/default/languages/en.ini | 1 + app/default/languages/no.ini | 1 + app/helpers.php | 15 ++---- app/plugins.php | 83 +++++++++++++++++++++++++++++++ app/plugins/global/languages.php | 85 ++++++++++++++++++++++++++++++++ app/rendering.php | 8 +++ app/router.php | 12 +++-- 9 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 app/plugins.php create mode 100644 app/plugins/global/languages.php diff --git a/app/config.php b/app/config.php index 8c61d9e..fba4263 100644 --- a/app/config.php +++ b/app/config.php @@ -7,6 +7,9 @@ function createContext(): Context { : __DIR__ . '/config.ini'; $config = parse_ini_file($configFile, true); + // Load global plugins + getPluginManager()->loadGlobalPlugins($config); + $defaultLang = $config['languages']['default'] ?? 'no'; $availableLangs = array_map('trim', explode(',', $config['languages']['available'] ?? 'no')); diff --git a/app/content.php b/app/content.php index c6e6a54..e902a0c 100644 --- a/app/content.php +++ b/app/content.php @@ -155,13 +155,7 @@ function loadMetadata(string $dirPath, string $lang, string $defaultLang): ?arra return $baseMetadata ?: null; } -function loadTranslations(string $lang): array { - $translationFile = dirname(__DIR__) . "/custom/languages/$lang.ini"; - if (file_exists($translationFile)) { - return parse_ini_file($translationFile) ?: []; - } - return []; -} + function buildNavigation(Context $ctx): array { $navItems = []; @@ -177,13 +171,11 @@ function buildNavigation(Context $ctx): array { } // Check if content exists for current language - if ($ctx->currentLang !== $ctx->defaultLang) { - $contentFiles = findAllContentFiles($itemPath, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs); + if ($ctx->currentLang !== $ctx->defaultLang && shouldHideUntranslated()) { + $hasLangContent = hasLanguageContent($itemPath, $ctx->currentLang, CONTENT_EXTENSIONS); + $hasLangMetadata = hasLanguageMetadata($itemPath, $ctx->currentLang); - // If no content files, check if metadata has title for this language - $hasContent = !empty($contentFiles) || ($metadata && isset($metadata['title'])); - - if (!$hasContent) continue; + if (!$hasLangContent && !$hasLangMetadata) continue; } // Extract title and build URL diff --git a/app/default/languages/en.ini b/app/default/languages/en.ini index 761b54b..db8f040 100644 --- a/app/default/languages/en.ini +++ b/app/default/languages/en.ini @@ -10,3 +10,4 @@ summary = "Summary" footer_text = "Footer content goes here" footer_handcoded = "This page was generated in" footer_page_time = "ms" +months = "January,February,March,April,May,June,July,August,September,October,November,December" diff --git a/app/default/languages/no.ini b/app/default/languages/no.ini index 514bf0a..cd7c114 100644 --- a/app/default/languages/no.ini +++ b/app/default/languages/no.ini @@ -10,3 +10,4 @@ summary = "Oppsummering" footer_text = "Bunntekst her" footer_handcoded = "Denne siden ble generert på" footer_page_time = "ms" +months = "januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember" diff --git a/app/helpers.php b/app/helpers.php index b95ff0b..94a1fd5 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -32,20 +32,11 @@ function extractTitle(string $filePath, string $lang, string $defaultLang): ?str return null; } -function formatNorwegianDate(string $dateString): string { - if (preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dateString, $matches)) { - $months = ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember']; - $day = (int)$matches[3]; - $month = $months[(int)$matches[2] - 1]; - $year = $matches[1]; - return "$day. $month $year"; - } - return $dateString; -} -function extractDateFromFolder(string $folderName): ?string { + +function extractDateFromFolder(string $folderName, string $lang): ?string { if (preg_match('/^(\d{4})-(\d{2})-(\d{2})-/', $folderName, $matches)) { - return formatNorwegianDate($matches[1] . '-' . $matches[2] . '-' . $matches[3]); + return formatDate($matches[1] . '-' . $matches[2] . '-' . $matches[3], $lang); } return null; } diff --git a/app/plugins.php b/app/plugins.php new file mode 100644 index 0000000..8cab6d9 --- /dev/null +++ b/app/plugins.php @@ -0,0 +1,83 @@ +loadPlugin($pluginName, 'global'); + } + } + } + + public function loadPagePlugins(?array $metadata): void { + if (!$metadata || !isset($metadata['plugins'])) return; + + $plugins = array_map('trim', explode(',', $metadata['plugins'])); + foreach ($plugins as $pluginName) { + if (!empty($pluginName)) { + $this->loadPlugin($pluginName, 'page'); + } + } + } + + private function loadPlugin(string $pluginName, string $scope): bool { + if (isset($this->loadedPlugins[$pluginName])) return true; + + $pluginName = basename($pluginName); + + $customPluginPath = __DIR__ . "/../custom/plugins/{$scope}/{$pluginName}.php"; + $appPluginPath = __DIR__ . "/plugins/{$scope}/{$pluginName}.php"; + + $pluginPath = file_exists($customPluginPath) ? $customPluginPath + : (file_exists($appPluginPath) ? $appPluginPath : null); + + if (!$pluginPath) { + error_log("Plugin not found: {$pluginName} (scope: {$scope})"); + return false; + } + + require_once $pluginPath; + + $this->loadedPlugins[$pluginName] = [ + 'path' => $pluginPath, + 'scope' => $scope, + 'loaded_at' => microtime(true) + ]; + + if ($scope === 'global') { + $this->globalPlugins[] = $pluginName; + } + + return true; + } + + public function getLoadedPlugins(): array { + return array_keys($this->loadedPlugins); + } + + public function getGlobalPlugins(): array { + return $this->globalPlugins; + } + + public function isLoaded(string $pluginName): bool { + return isset($this->loadedPlugins[$pluginName]); + } + + public function getPluginInfo(string $pluginName): ?array { + return $this->loadedPlugins[$pluginName] ?? null; + } +} + +$GLOBALS['pluginManager'] = new PluginManager(); + +function getPluginManager(): PluginManager { + return $GLOBALS['pluginManager']; +} diff --git a/app/plugins/global/languages.php b/app/plugins/global/languages.php new file mode 100644 index 0000000..2b4ded2 --- /dev/null +++ b/app/plugins/global/languages.php @@ -0,0 +1,85 @@ += 3) { + $fileLang = $parts[count($parts) - 2]; + if ($fileLang === $lang && is_file("$dirPath/$file")) { + return true; + } + } + } + return false; +} + +function formatDate(string $dateString, string $lang): string { + if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dateString, $matches)) { + return $dateString; + } + + $translations = loadTranslations($lang); + $day = (int)$matches[3]; + $monthIndex = (int)$matches[2] - 1; + $year = $matches[1]; + + if (isset($translations['months'])) { + $months = array_map('trim', explode(',', $translations['months'])); + $month = $months[$monthIndex] ?? $matches[2]; + } else { + $month = $matches[2]; + } + + return "$day. $month $year"; +} diff --git a/app/rendering.php b/app/rendering.php index cef9332..da4133d 100644 --- a/app/rendering.php +++ b/app/rendering.php @@ -56,6 +56,10 @@ function renderFile(Context $ctx, string $filePath): void { $pageDir = dirname($realPath); $pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang); + + // Load page-level plugins + getPluginManager()->loadPagePlugins($pageMetadata); + $pageTitle = $pageMetadata['title'] ?? null; $metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang); @@ -108,6 +112,10 @@ function renderMultipleFiles(Context $ctx, array $filePaths, string $pageDir): v $translations = $ctx->translations; $pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang); + + // Load page-level plugins + getPluginManager()->loadPagePlugins($pageMetadata); + $pageTitle = $pageMetadata['title'] ?? null; $metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang); diff --git a/app/router.php b/app/router.php index a4a279e..a69a161 100644 --- a/app/router.php +++ b/app/router.php @@ -4,6 +4,7 @@ require_once __DIR__ . '/constants.php'; require_once __DIR__ . '/context.php'; require_once __DIR__ . '/helpers.php'; +require_once __DIR__ . '/plugins.php'; require_once __DIR__ . '/config.php'; require_once __DIR__ . '/content.php'; require_once __DIR__ . '/rendering.php'; @@ -90,9 +91,10 @@ switch ($parsedPath['type']) { $itemPath = "$dir/$item"; // Check if content exists for current language - if ($ctx->currentLang !== $ctx->defaultLang) { - $contentFiles = findAllContentFiles($itemPath, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs); - if (empty($contentFiles)) return null; + 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); @@ -102,9 +104,9 @@ switch ($parsedPath['type']) { $title = $metadata['title'] ?? extractTitle($itemPath, $ctx->currentLang, $ctx->defaultLang) ?? $item; $date = null; if (isset($metadata['date'])) { - $date = formatNorwegianDate($metadata['date']); + $date = formatDate($metadata['date'], $ctx->currentLang); } else { - $date = extractDateFromFolder($item) ?: date("F d, Y", filemtime($itemPath)); + $date = extractDateFromFolder($item, $ctx->currentLang) ?: date("F d, Y", filemtime($itemPath)); } // Use translated slug if available, otherwise use folder name