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
This commit is contained in:
parent
875408a27c
commit
24ee209e17
9 changed files with 196 additions and 30 deletions
|
|
@ -7,6 +7,9 @@ function createContext(): Context {
|
||||||
: __DIR__ . '/config.ini';
|
: __DIR__ . '/config.ini';
|
||||||
$config = parse_ini_file($configFile, true);
|
$config = parse_ini_file($configFile, true);
|
||||||
|
|
||||||
|
// Load global plugins
|
||||||
|
getPluginManager()->loadGlobalPlugins($config);
|
||||||
|
|
||||||
$defaultLang = $config['languages']['default'] ?? 'no';
|
$defaultLang = $config['languages']['default'] ?? 'no';
|
||||||
$availableLangs = array_map('trim', explode(',', $config['languages']['available'] ?? 'no'));
|
$availableLangs = array_map('trim', explode(',', $config['languages']['available'] ?? 'no'));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,13 +155,7 @@ function loadMetadata(string $dirPath, string $lang, string $defaultLang): ?arra
|
||||||
return $baseMetadata ?: null;
|
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 {
|
function buildNavigation(Context $ctx): array {
|
||||||
$navItems = [];
|
$navItems = [];
|
||||||
|
|
@ -177,13 +171,11 @@ function buildNavigation(Context $ctx): array {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if content exists for current language
|
// Check if content exists for current language
|
||||||
if ($ctx->currentLang !== $ctx->defaultLang) {
|
if ($ctx->currentLang !== $ctx->defaultLang && shouldHideUntranslated()) {
|
||||||
$contentFiles = findAllContentFiles($itemPath, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs);
|
$hasLangContent = hasLanguageContent($itemPath, $ctx->currentLang, CONTENT_EXTENSIONS);
|
||||||
|
$hasLangMetadata = hasLanguageMetadata($itemPath, $ctx->currentLang);
|
||||||
|
|
||||||
// If no content files, check if metadata has title for this language
|
if (!$hasLangContent && !$hasLangMetadata) continue;
|
||||||
$hasContent = !empty($contentFiles) || ($metadata && isset($metadata['title']));
|
|
||||||
|
|
||||||
if (!$hasContent) continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract title and build URL
|
// Extract title and build URL
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@ summary = "Summary"
|
||||||
footer_text = "Footer content goes here"
|
footer_text = "Footer content goes here"
|
||||||
footer_handcoded = "This page was generated in"
|
footer_handcoded = "This page was generated in"
|
||||||
footer_page_time = "ms"
|
footer_page_time = "ms"
|
||||||
|
months = "January,February,March,April,May,June,July,August,September,October,November,December"
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@ summary = "Oppsummering"
|
||||||
footer_text = "Bunntekst her"
|
footer_text = "Bunntekst her"
|
||||||
footer_handcoded = "Denne siden ble generert på"
|
footer_handcoded = "Denne siden ble generert på"
|
||||||
footer_page_time = "ms"
|
footer_page_time = "ms"
|
||||||
|
months = "januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember"
|
||||||
|
|
|
||||||
|
|
@ -32,20 +32,11 @@ function extractTitle(string $filePath, string $lang, string $defaultLang): ?str
|
||||||
return null;
|
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)) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
83
app/plugins.php
Normal file
83
app/plugins.php
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Plugin System - manages global and page-level plugins
|
||||||
|
|
||||||
|
class PluginManager {
|
||||||
|
private array $loadedPlugins = [];
|
||||||
|
private array $globalPlugins = [];
|
||||||
|
|
||||||
|
public function loadGlobalPlugins(array $config): void {
|
||||||
|
if (!isset($config['plugins']['enabled'])) return;
|
||||||
|
|
||||||
|
$plugins = array_map('trim', explode(',', $config['plugins']['enabled']));
|
||||||
|
foreach ($plugins as $pluginName) {
|
||||||
|
if (!empty($pluginName)) {
|
||||||
|
$this->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'];
|
||||||
|
}
|
||||||
85
app/plugins/global/languages.php
Normal file
85
app/plugins/global/languages.php
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Languages plugin - translation loading and filtering
|
||||||
|
|
||||||
|
function loadTranslations(string $lang): array {
|
||||||
|
$defaultTranslationFile = dirname(__DIR__, 2) . "/default/languages/$lang.ini";
|
||||||
|
$customTranslationFile = dirname(__DIR__, 3) . "/custom/languages/$lang.ini";
|
||||||
|
|
||||||
|
$translations = [];
|
||||||
|
|
||||||
|
if (file_exists($defaultTranslationFile)) {
|
||||||
|
$translations = parse_ini_file($defaultTranslationFile) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($customTranslationFile)) {
|
||||||
|
$customTranslations = parse_ini_file($customTranslationFile) ?: [];
|
||||||
|
$translations = array_merge($translations, $customTranslations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldHideUntranslated(): bool {
|
||||||
|
$configFile = file_exists(__DIR__ . '/../../../custom/config.ini')
|
||||||
|
? __DIR__ . '/../../../custom/config.ini'
|
||||||
|
: __DIR__ . '/../../config.ini';
|
||||||
|
|
||||||
|
if (!file_exists($configFile)) return true;
|
||||||
|
|
||||||
|
$config = parse_ini_file($configFile, true);
|
||||||
|
return !isset($config['languages']['show_untranslated'])
|
||||||
|
|| $config['languages']['show_untranslated'] !== 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasLanguageMetadata(string $dirPath, string $lang): bool {
|
||||||
|
$metadataFile = "$dirPath/metadata.ini";
|
||||||
|
if (!file_exists($metadataFile)) return false;
|
||||||
|
|
||||||
|
$metadata = parse_ini_file($metadataFile, true);
|
||||||
|
if (!$metadata) return false;
|
||||||
|
|
||||||
|
if (!isset($metadata[$lang]) || !is_array($metadata[$lang])) return false;
|
||||||
|
|
||||||
|
// Check if language section has meaningful content (title or summary)
|
||||||
|
return isset($metadata[$lang]['title']) || isset($metadata[$lang]['summary']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasLanguageContent(string $dirPath, string $lang, array $contentExtensions): bool {
|
||||||
|
if (!is_dir($dirPath)) return false;
|
||||||
|
|
||||||
|
$files = scandir($dirPath) ?: [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||||
|
if (!in_array($ext, $contentExtensions)) continue;
|
||||||
|
|
||||||
|
$parts = explode('.', $file);
|
||||||
|
if (count($parts) >= 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";
|
||||||
|
}
|
||||||
|
|
@ -56,6 +56,10 @@ function renderFile(Context $ctx, string $filePath): void {
|
||||||
|
|
||||||
$pageDir = dirname($realPath);
|
$pageDir = dirname($realPath);
|
||||||
$pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang);
|
$pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang);
|
||||||
|
|
||||||
|
// Load page-level plugins
|
||||||
|
getPluginManager()->loadPagePlugins($pageMetadata);
|
||||||
|
|
||||||
$pageTitle = $pageMetadata['title'] ?? null;
|
$pageTitle = $pageMetadata['title'] ?? null;
|
||||||
$metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang);
|
$metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang);
|
||||||
|
|
||||||
|
|
@ -108,6 +112,10 @@ function renderMultipleFiles(Context $ctx, array $filePaths, string $pageDir): v
|
||||||
$translations = $ctx->translations;
|
$translations = $ctx->translations;
|
||||||
|
|
||||||
$pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang);
|
$pageMetadata = loadMetadata($pageDir, $ctx->currentLang, $ctx->defaultLang);
|
||||||
|
|
||||||
|
// Load page-level plugins
|
||||||
|
getPluginManager()->loadPagePlugins($pageMetadata);
|
||||||
|
|
||||||
$pageTitle = $pageMetadata['title'] ?? null;
|
$pageTitle = $pageMetadata['title'] ?? null;
|
||||||
$metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang);
|
$metaDescription = extractMetaDescription($pageDir, $pageMetadata, $ctx->currentLang, $ctx->defaultLang);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
require_once __DIR__ . '/constants.php';
|
require_once __DIR__ . '/constants.php';
|
||||||
require_once __DIR__ . '/context.php';
|
require_once __DIR__ . '/context.php';
|
||||||
require_once __DIR__ . '/helpers.php';
|
require_once __DIR__ . '/helpers.php';
|
||||||
|
require_once __DIR__ . '/plugins.php';
|
||||||
require_once __DIR__ . '/config.php';
|
require_once __DIR__ . '/config.php';
|
||||||
require_once __DIR__ . '/content.php';
|
require_once __DIR__ . '/content.php';
|
||||||
require_once __DIR__ . '/rendering.php';
|
require_once __DIR__ . '/rendering.php';
|
||||||
|
|
@ -90,9 +91,10 @@ switch ($parsedPath['type']) {
|
||||||
$itemPath = "$dir/$item";
|
$itemPath = "$dir/$item";
|
||||||
|
|
||||||
// Check if content exists for current language
|
// Check if content exists for current language
|
||||||
if ($ctx->currentLang !== $ctx->defaultLang) {
|
if ($ctx->currentLang !== $ctx->defaultLang && shouldHideUntranslated()) {
|
||||||
$contentFiles = findAllContentFiles($itemPath, $ctx->currentLang, $ctx->defaultLang, $ctx->availableLangs);
|
$hasLangContent = hasLanguageContent($itemPath, $ctx->currentLang, CONTENT_EXTENSIONS);
|
||||||
if (empty($contentFiles)) return null;
|
$hasLangMetadata = hasLanguageMetadata($itemPath, $ctx->currentLang);
|
||||||
|
if (!$hasLangContent && !$hasLangMetadata) return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata = loadMetadata($itemPath, $ctx->currentLang, $ctx->defaultLang);
|
$metadata = loadMetadata($itemPath, $ctx->currentLang, $ctx->defaultLang);
|
||||||
|
|
@ -102,9 +104,9 @@ switch ($parsedPath['type']) {
|
||||||
$title = $metadata['title'] ?? extractTitle($itemPath, $ctx->currentLang, $ctx->defaultLang) ?? $item;
|
$title = $metadata['title'] ?? extractTitle($itemPath, $ctx->currentLang, $ctx->defaultLang) ?? $item;
|
||||||
$date = null;
|
$date = null;
|
||||||
if (isset($metadata['date'])) {
|
if (isset($metadata['date'])) {
|
||||||
$date = formatNorwegianDate($metadata['date']);
|
$date = formatDate($metadata['date'], $ctx->currentLang);
|
||||||
} else {
|
} 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
|
// Use translated slug if available, otherwise use folder name
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue