folderweb/app/router.php
Ruben 8857a91406 Add language prefix to content URLs and use folder names for assets
Use language prefix for all content URLs and ensure assets reference
folder names instead of translated slugs for proper file access
2025-11-29 01:45:41 +01:00

201 lines
7 KiB
PHP

<?php
// 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';
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/content.php';
require_once __DIR__ . '/rendering.php';
// 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)) {
header('Content-Type: ' . (mime_content_type($assetPath) ?: 'application/octet-stream'));
readfile($assetPath);
exit;
}
// Check for assets in content directory (CSS, images, etc.)
$contentAssetPath = $ctx->contentDir . '/' . $ctx->requestPath;
if (file_exists($contentAssetPath) && is_file($contentAssetPath)) {
$ext = pathinfo($contentAssetPath, PATHINFO_EXTENSION);
// Define MIME types for asset files
$mimeTypes = [
'css' => 'text/css',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'pdf' => 'application/pdf',
'woff' => 'font/woff',
'woff2' => 'font/woff2',
'ttf' => 'font/ttf',
'otf' => 'font/otf',
];
$extLower = strtolower($ext);
if (isset($mimeTypes[$extLower])) {
header('Content-Type: ' . $mimeTypes[$extLower]);
readfile($contentAssetPath);
exit;
}
}
// Handle frontpage
if (empty($ctx->requestPath)) {
$contentFiles = findAllContentFiles($ctx->contentDir);
if (!empty($contentFiles)) {
renderMultipleFiles($ctx, $contentFiles, $ctx->contentDir);
}
}
// Parse and handle request
$parsedPath = parseRequestPath($ctx);
switch ($parsedPath['type']) {
case 'page':
$dir = $parsedPath['path'];
// 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);
if (!empty($contentFiles)) {
$pageContent = implode('', array_map('renderContentFile', $contentFiles));
}
// Load metadata for this directory
$metadata = loadMetadata($dir);
// Check if hide_list is enabled - if so, treat as page
if (isset($metadata['hide_list']) && $metadata['hide_list']) {
if (!empty($contentFiles)) {
renderMultipleFiles($ctx, $contentFiles, $dir);
}
break;
}
// 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'];
if (!str_ends_with($templateName, '.php')) {
$templateName .= '';
}
$customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php";
$defaultTemplate = __DIR__ . "/default/templates/$templateName.php";
if (file_exists($customTemplate)) {
$listTemplate = $customTemplate;
} elseif (file_exists($defaultTemplate)) {
$listTemplate = $defaultTemplate;
}
}
// Build list items
$subdirs = getSubdirectories($dir);
$items = array_filter(array_map(function($item) use ($dir, $ctx) {
$itemPath = "$dir/$item";
$metadata = loadMetadata($itemPath);
$coverImage = findCoverImage($itemPath);
$pdfFile = findPdfFile($itemPath);
$title = $metadata['title'] ?? extractTitle($itemPath) ?? $item;
$date = null;
if (isset($metadata['date'])) {
$date = $metadata['date'];
// Let plugins format date
$date = Hooks::apply(Hook::PROCESS_CONTENT, $date, 'date_format');
} else {
$date = extractDateFromFolder($item) ?: date("F d, Y", filemtime($itemPath));
}
// Use slug if available
$urlSlug = ($metadata && isset($metadata['slug'])) ? $metadata['slug'] : $item;
$langPrefix = $ctx->get('langPrefix', '');
$baseUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($urlSlug);
// Assets (cover, PDF) must use actual folder name, not translated slug
$assetUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($item);
return [
'title' => $title,
'url' => $baseUrl . '/',
'date' => $date,
'summary' => $metadata['summary'] ?? null,
'cover' => $coverImage ? "$assetUrl/$coverImage" : null,
'pdf' => $pdfFile ? "$assetUrl/$pdfFile" : null,
'redirect' => $metadata['redirect'] ?? null
];
}, $subdirs));
// Sort by date - check metadata for order preference
$sortOrder = strtolower($metadata['order'] ?? 'descending');
if ($sortOrder === 'ascending') {
usort($items, fn($a, $b) => strcmp($a['date'] ?? '', $b['date'] ?? ''));
} else {
// Default: descending (newest first)
usort($items, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
}
// Prepare all variables for base template
$navigation = $ctx->navigation;
$homeLabel = $ctx->homeLabel;
$pageTitle = $metadata['title'] ?? null;
$metaDescription = extractMetaDescription($dir, $metadata);
// Check for page-specific CSS
$pageCss = findPageCss($dir, $ctx->contentDir);
// 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();
renderTemplate($ctx, $content);
break;
case 'not_found':
http_response_code(404);
renderTemplate($ctx, "<h1>404 - Page Not Found</h1><p>The requested page could not be found.</p>", 404);
break;
}