folderweb/app/content.php
Ruben 149ba03359 Add modular architecture to router
Extract configuration, helpers, content processing, and rendering logic
into separate files

Refactor router to use modular components
2025-11-01 22:50:02 +01:00

225 lines
7.8 KiB
PHP

<?php
// Find all content files in a directory (supporting language variants)
function findAllContentFiles(string $dir, string $lang, string $defaultLang): array {
if (!is_dir($dir)) return [];
$files = scandir($dir) ?: [];
$contentFiles = [];
$extensions = ['md', 'html', 'php'];
foreach ($files as $file) {
if ($file === '.' || $file === '..') continue;
// Exclude system files from content
if ($file === 'index.php') continue;
$ext = pathinfo($file, PATHINFO_EXTENSION);
if (!in_array($ext, $extensions)) continue;
$filePath = "$dir/$file";
if (!is_file($filePath)) continue;
// Parse filename to check for language variant
$parts = explode('.', $file);
// Check if this is a language-specific file
if (count($parts) >= 3) {
// Pattern: name.lang.ext
$fileLang = $parts[count($parts) - 2];
if (in_array($fileLang, ['no', 'en'])) {
// Only include if it matches current language
if ($fileLang === $lang) {
$contentFiles[] = [
'path' => $filePath,
'name' => $file,
'sort_key' => $parts[0] // Use base name for sorting
];
}
continue;
}
}
// Default files (no language suffix) - include if current lang is default
// or if no language-specific version exists
$baseName = $parts[0];
$hasLangVersion = false;
if ($lang !== $defaultLang) {
// Check if language-specific version exists
foreach ($extensions as $checkExt) {
if (file_exists("$dir/$baseName.$lang.$checkExt")) {
$hasLangVersion = true;
break;
}
}
}
if (!$hasLangVersion) {
$contentFiles[] = [
'path' => $filePath,
'name' => $file,
'sort_key' => $baseName
];
}
}
// Sort by filename (alphanumerical)
usort($contentFiles, fn($a, $b) => strnatcmp($a['sort_key'], $b['sort_key']));
return array_column($contentFiles, 'path');
}
function resolveTranslatedPath(string $requestPath, string $contentDir, string $lang, string $defaultLang): string {
// If default language, no translation needed
if ($lang === $defaultLang) {
return $requestPath;
}
$parts = explode('/', trim($requestPath, '/'));
$resolvedParts = [];
$currentPath = $contentDir;
foreach ($parts as $segment) {
if (empty($segment)) continue;
// Check all subdirectories for slug matches
$found = false;
if (is_dir($currentPath)) {
$subdirs = array_filter(
scandir($currentPath) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$currentPath/$item")
);
foreach ($subdirs as $dir) {
$metadata = loadMetadata("$currentPath/$dir", $lang, $defaultLang);
if ($metadata && isset($metadata['slug']) && $metadata['slug'] === $segment) {
$resolvedParts[] = $dir;
$currentPath .= "/$dir";
$found = true;
break;
}
}
}
// If no slug match, use segment as-is
if (!$found) {
$resolvedParts[] = $segment;
$currentPath .= "/$segment";
}
}
return implode('/', $resolvedParts);
}
function parseRequestPath(string $requestPath, string $contentDir, bool $hasTrailingSlash, string $lang, string $defaultLang): array {
// Resolve translated slugs to actual directory names
$resolvedPath = resolveTranslatedPath($requestPath, $contentDir, $lang, $defaultLang);
$contentPath = rtrim($contentDir, '/') . '/' . ltrim($resolvedPath, '/');
if (is_file($contentPath)) {
return ['type' => 'file', 'path' => realpath($contentPath)];
}
if (is_dir($contentPath)) {
// Check if directory has subdirectories
$hasSubdirs = !empty(array_filter(
scandir($contentPath) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$contentPath/$item")
));
// If directory has subdirectories, it's an article-type folder (list view)
if ($hasSubdirs) {
return ['type' => 'directory', 'path' => realpath($contentPath)];
}
// No subdirectories - it's a page-type folder
// Find all content files in this directory
$contentFiles = findAllContentFiles($contentPath, $lang, $defaultLang);
if (!empty($contentFiles)) {
return ['type' => 'page', 'path' => realpath($contentPath), 'files' => $contentFiles, 'needsSlash' => !$hasTrailingSlash];
}
// No content files found
return ['type' => 'directory', 'path' => realpath($contentPath)];
}
return ['type' => 'not_found', 'path' => $contentPath];
}
function loadMetadata(string $dirPath, string $lang, string $defaultLang): ?array {
$metadataFile = "$dirPath/metadata.ini";
if (!file_exists($metadataFile)) return null;
$metadata = parse_ini_file($metadataFile, true);
if (!$metadata) return null;
// Extract base metadata (non-section values)
$baseMetadata = array_filter($metadata, fn($key) => !is_array($metadata[$key]), ARRAY_FILTER_USE_KEY);
// If current language is not default, merge language-specific overrides
if ($lang !== $defaultLang && isset($metadata[$lang]) && is_array($metadata[$lang])) {
$baseMetadata = array_merge($baseMetadata, $metadata[$lang]);
}
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(string $contentDir, string $currentLang, string $defaultLang): array {
$navItems = [];
// Scan top-level directories in content
$items = array_filter(
scandir($contentDir) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$contentDir/$item")
);
foreach ($items as $item) {
$itemPath = "$contentDir/$item";
$metadata = loadMetadata($itemPath, $currentLang, $defaultLang);
// Check if this item should be in menu
if (!$metadata || empty($metadata['menu'])) {
continue;
}
// Check if content exists for current language
if ($currentLang !== $defaultLang) {
$contentFiles = findAllContentFiles($itemPath, $currentLang, $defaultLang);
// If no content files, check if metadata has title for this language
$hasContent = !empty($contentFiles) || ($metadata && isset($metadata['title']));
if (!$hasContent) continue;
}
// Extract title and build URL
$title = $metadata['title'] ?? extractTitle($itemPath, $currentLang, $defaultLang) ?? ucfirst($item);
$langPrefix = $currentLang !== $defaultLang ? "/$currentLang" : '';
// Use translated slug if available
$urlSlug = ($currentLang !== $defaultLang && $metadata && isset($metadata['slug']))
? $metadata['slug']
: $item;
$navItems[] = [
'title' => $title,
'url' => $langPrefix . '/' . urlencode($urlSlug) . '/',
'order' => (int)($metadata['menu_order'] ?? 999)
];
}
// Sort by menu_order
usort($navItems, fn($a, $b) => $a['order'] <=> $b['order']);
return $navItems;
}