Add constants for file extensions Extract helper functions for common operations Improve PDF file detection Simplify directory scanning operations Standardize template resolution Optimize content rendering pipeline
211 lines
7.2 KiB
PHP
211 lines
7.2 KiB
PHP
<?php
|
|
|
|
// Find all content files in a directory (supporting language variants)
|
|
function findAllContentFiles(string $dir, string $lang, string $defaultLang): array {
|
|
global $availableLangs;
|
|
|
|
if (!is_dir($dir)) return [];
|
|
|
|
$files = scandir($dir) ?: [];
|
|
$contentFiles = [];
|
|
|
|
foreach ($files as $file) {
|
|
if ($file === '.' || $file === '..' || $file === 'index.php') continue;
|
|
|
|
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
|
if (!in_array($ext, CONTENT_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, $availableLangs)) {
|
|
// Only include if it matches current language
|
|
if ($fileLang === $lang) {
|
|
$contentFiles[] = [
|
|
'path' => $filePath,
|
|
'name' => $file,
|
|
'sort_key' => $parts[0]
|
|
];
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Default files (no language suffix) - include if no language-specific version exists
|
|
$baseName = $parts[0];
|
|
$hasLangVersion = false;
|
|
|
|
if ($lang !== $defaultLang) {
|
|
// Check if language-specific version exists
|
|
foreach (CONTENT_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 = getSubdirectories($currentPath);
|
|
|
|
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(getSubdirectories($contentPath));
|
|
|
|
// 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 = [];
|
|
$items = getSubdirectories($contentDir);
|
|
|
|
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 = getLangPrefix($currentLang, $defaultLang);
|
|
|
|
// 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;
|
|
}
|