Refactor template and content handling logic

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
This commit is contained in:
Ruben 2025-11-01 22:54:42 +01:00
parent 149ba03359
commit 32449d2edd
6 changed files with 98 additions and 145 deletions

View file

@ -14,12 +14,7 @@ $userContentDir = $_SERVER['DOCUMENT_ROOT'];
$demoContentDir = __DIR__ . '/default/content'; $demoContentDir = __DIR__ . '/default/content';
// Check if user content directory has actual content (more than just . and ..) // Check if user content directory has actual content (more than just . and ..)
$hasUserContent = false; $hasUserContent = is_dir($userContentDir) && count(scandir($userContentDir) ?: []) > 2;
if (is_dir($userContentDir)) {
$userFiles = scandir($userContentDir) ?: [];
$userFiles = array_diff($userFiles, ['.', '..']);
$hasUserContent = count($userFiles) > 0;
}
$contentDir = $hasUserContent ? realpath($userContentDir) : realpath($demoContentDir); $contentDir = $hasUserContent ? realpath($userContentDir) : realpath($demoContentDir);
@ -37,15 +32,7 @@ if (!empty($pathParts[0]) && in_array($pathParts[0], $availableLangs) && $pathPa
$requestPath = implode('/', $pathParts); $requestPath = implode('/', $pathParts);
} }
// Use custom templates with fallback to defaults // Resolve templates with custom fallback to defaults
$customBaseTemplate = dirname(__DIR__) . '/custom/templates/base.php'; $baseTemplate = resolveTemplate('base');
$defaultBaseTemplate = __DIR__ . '/default/templates/base.php'; $pageTemplate = resolveTemplate('page');
$baseTemplate = file_exists($customBaseTemplate) ? $customBaseTemplate : $defaultBaseTemplate; $listTemplate = resolveTemplate('list');
$customPageTemplate = dirname(__DIR__) . '/custom/templates/page.php';
$defaultPageTemplate = __DIR__ . '/default/templates/page.php';
$pageTemplate = file_exists($customPageTemplate) ? $customPageTemplate : $defaultPageTemplate;
$customListTemplate = dirname(__DIR__) . '/custom/templates/list.php';
$defaultListTemplate = __DIR__ . '/default/templates/list.php';
$listTemplate = file_exists($customListTemplate) ? $customListTemplate : $defaultListTemplate;

7
app/constants.php Normal file
View file

@ -0,0 +1,7 @@
<?php
// Content file extensions
define('CONTENT_EXTENSIONS', ['md', 'html', 'php']);
// Cover image extensions
define('COVER_IMAGE_EXTENSIONS', ['jpg', 'jpeg', 'png', 'webp', 'gif']);

View file

@ -2,20 +2,18 @@
// Find all content files in a directory (supporting language variants) // Find all content files in a directory (supporting language variants)
function findAllContentFiles(string $dir, string $lang, string $defaultLang): array { function findAllContentFiles(string $dir, string $lang, string $defaultLang): array {
global $availableLangs;
if (!is_dir($dir)) return []; if (!is_dir($dir)) return [];
$files = scandir($dir) ?: []; $files = scandir($dir) ?: [];
$contentFiles = []; $contentFiles = [];
$extensions = ['md', 'html', 'php'];
foreach ($files as $file) { foreach ($files as $file) {
if ($file === '.' || $file === '..') continue; if ($file === '.' || $file === '..' || $file === 'index.php') continue;
// Exclude system files from content
if ($file === 'index.php') continue;
$ext = pathinfo($file, PATHINFO_EXTENSION); $ext = pathinfo($file, PATHINFO_EXTENSION);
if (!in_array($ext, $extensions)) continue; if (!in_array($ext, CONTENT_EXTENSIONS)) continue;
$filePath = "$dir/$file"; $filePath = "$dir/$file";
if (!is_file($filePath)) continue; if (!is_file($filePath)) continue;
@ -27,27 +25,26 @@ function findAllContentFiles(string $dir, string $lang, string $defaultLang): ar
if (count($parts) >= 3) { if (count($parts) >= 3) {
// Pattern: name.lang.ext // Pattern: name.lang.ext
$fileLang = $parts[count($parts) - 2]; $fileLang = $parts[count($parts) - 2];
if (in_array($fileLang, ['no', 'en'])) { if (in_array($fileLang, $availableLangs)) {
// Only include if it matches current language // Only include if it matches current language
if ($fileLang === $lang) { if ($fileLang === $lang) {
$contentFiles[] = [ $contentFiles[] = [
'path' => $filePath, 'path' => $filePath,
'name' => $file, 'name' => $file,
'sort_key' => $parts[0] // Use base name for sorting 'sort_key' => $parts[0]
]; ];
} }
continue; continue;
} }
} }
// Default files (no language suffix) - include if current lang is default // Default files (no language suffix) - include if no language-specific version exists
// or if no language-specific version exists
$baseName = $parts[0]; $baseName = $parts[0];
$hasLangVersion = false; $hasLangVersion = false;
if ($lang !== $defaultLang) { if ($lang !== $defaultLang) {
// Check if language-specific version exists // Check if language-specific version exists
foreach ($extensions as $checkExt) { foreach (CONTENT_EXTENSIONS as $checkExt) {
if (file_exists("$dir/$baseName.$lang.$checkExt")) { if (file_exists("$dir/$baseName.$lang.$checkExt")) {
$hasLangVersion = true; $hasLangVersion = true;
break; break;
@ -86,10 +83,7 @@ function resolveTranslatedPath(string $requestPath, string $contentDir, string $
// Check all subdirectories for slug matches // Check all subdirectories for slug matches
$found = false; $found = false;
if (is_dir($currentPath)) { if (is_dir($currentPath)) {
$subdirs = array_filter( $subdirs = getSubdirectories($currentPath);
scandir($currentPath) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$currentPath/$item")
);
foreach ($subdirs as $dir) { foreach ($subdirs as $dir) {
$metadata = loadMetadata("$currentPath/$dir", $lang, $defaultLang); $metadata = loadMetadata("$currentPath/$dir", $lang, $defaultLang);
@ -123,10 +117,7 @@ function parseRequestPath(string $requestPath, string $contentDir, bool $hasTrai
if (is_dir($contentPath)) { if (is_dir($contentPath)) {
// Check if directory has subdirectories // Check if directory has subdirectories
$hasSubdirs = !empty(array_filter( $hasSubdirs = !empty(getSubdirectories($contentPath));
scandir($contentPath) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$contentPath/$item")
));
// If directory has subdirectories, it's an article-type folder (list view) // If directory has subdirectories, it's an article-type folder (list view)
if ($hasSubdirs) { if ($hasSubdirs) {
@ -176,12 +167,7 @@ function loadTranslations(string $lang): array {
function buildNavigation(string $contentDir, string $currentLang, string $defaultLang): array { function buildNavigation(string $contentDir, string $currentLang, string $defaultLang): array {
$navItems = []; $navItems = [];
$items = getSubdirectories($contentDir);
// Scan top-level directories in content
$items = array_filter(
scandir($contentDir) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$contentDir/$item")
);
foreach ($items as $item) { foreach ($items as $item) {
$itemPath = "$contentDir/$item"; $itemPath = "$contentDir/$item";
@ -204,7 +190,7 @@ function buildNavigation(string $contentDir, string $currentLang, string $defaul
// Extract title and build URL // Extract title and build URL
$title = $metadata['title'] ?? extractTitle($itemPath, $currentLang, $defaultLang) ?? ucfirst($item); $title = $metadata['title'] ?? extractTitle($itemPath, $currentLang, $defaultLang) ?? ucfirst($item);
$langPrefix = $currentLang !== $defaultLang ? "/$currentLang" : ''; $langPrefix = getLangPrefix($currentLang, $defaultLang);
// Use translated slug if available // Use translated slug if available
$urlSlug = ($currentLang !== $defaultLang && $metadata && isset($metadata['slug'])) $urlSlug = ($currentLang !== $defaultLang && $metadata && isset($metadata['slug']))

View file

@ -1,5 +1,23 @@
<?php <?php
function resolveTemplate(string $templateName): string {
$customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php";
$defaultTemplate = __DIR__ . "/default/templates/$templateName.php";
return file_exists($customTemplate) ? $customTemplate : $defaultTemplate;
}
function getSubdirectories(string $dir): array {
if (!is_dir($dir)) return [];
return array_filter(
scandir($dir) ?: [],
fn($item) => !in_array($item, ['.', '..']) && is_dir("$dir/$item")
);
}
function getLangPrefix(string $currentLang, string $defaultLang): string {
return $currentLang !== $defaultLang ? "/$currentLang" : '';
}
function extractTitle(string $filePath, string $lang, string $defaultLang): ?string { function extractTitle(string $filePath, string $lang, string $defaultLang): ?string {
$files = findAllContentFiles($filePath, $lang, $defaultLang); $files = findAllContentFiles($filePath, $lang, $defaultLang);
if (empty($files)) return null; if (empty($files)) return null;
@ -37,8 +55,7 @@ function extractDateFromFolder(string $folderName): ?string {
} }
function findCoverImage(string $dirPath): ?string { function findCoverImage(string $dirPath): ?string {
$extensions = ['jpg', 'jpeg', 'png', 'webp', 'gif']; foreach (COVER_IMAGE_EXTENSIONS as $ext) {
foreach ($extensions as $ext) {
if (file_exists("$dirPath/cover.$ext")) { if (file_exists("$dirPath/cover.$ext")) {
return "cover.$ext"; return "cover.$ext";
} }
@ -47,11 +64,6 @@ function findCoverImage(string $dirPath): ?string {
} }
function findPdfFile(string $dirPath): ?string { function findPdfFile(string $dirPath): ?string {
$files = scandir($dirPath) ?: []; $pdfs = glob("$dirPath/*.pdf");
foreach ($files as $file) { return $pdfs ? basename($pdfs[0]) : null;
if (pathinfo($file, PATHINFO_EXTENSION) === 'pdf') {
return $file;
}
}
return null;
} }

View file

@ -1,18 +1,35 @@
<?php <?php
function prepareTemplateContext(): array {
global $contentDir, $currentLang, $defaultLang;
return [
'navigation' => buildNavigation($contentDir, $currentLang, $defaultLang),
'homeLabel' => loadMetadata($contentDir, $currentLang, $defaultLang)['slug'] ?? 'Home',
'translations' => loadTranslations($currentLang)
];
}
function renderContentFile(string $filePath): string {
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
ob_start();
if ($ext === 'md') {
if (!class_exists('Parsedown')) {
require_once __DIR__ . '/vendor/Parsedown.php';
}
echo '<article>' . (new Parsedown())->text(file_get_contents($filePath)) . '</article>';
} elseif (in_array($ext, ['html', 'php'])) {
include $filePath;
}
return ob_get_clean();
}
function renderTemplate(string $content, int $statusCode = 200): void { function renderTemplate(string $content, int $statusCode = 200): void {
global $baseTemplate, $contentDir, $currentLang, $defaultLang; global $baseTemplate;
// Build navigation for templates extract(prepareTemplateContext());
$navigation = buildNavigation($contentDir, $currentLang, $defaultLang);
// Load frontpage metadata for home button label
$frontpageMetadata = loadMetadata($contentDir, $currentLang, $defaultLang);
$homeLabel = $frontpageMetadata['slug'] ?? 'Home';
// Load translations
$translations = loadTranslations($currentLang);
http_response_code($statusCode); http_response_code($statusCode);
include $baseTemplate; include $baseTemplate;
exit; exit;
@ -28,33 +45,16 @@ function renderFile(string $filePath): void {
$ext = pathinfo($realPath, PATHINFO_EXTENSION); $ext = pathinfo($realPath, PATHINFO_EXTENSION);
if (in_array($ext, ['php', 'html', 'md'])) { if (in_array($ext, CONTENT_EXTENSIONS)) {
ob_start(); $content = renderContentFile($realPath);
if ($ext === 'md') {
if (!class_exists('Parsedown')) {
require_once __DIR__ . '/vendor/Parsedown.php';
}
echo '<article>' . (new Parsedown())->text(file_get_contents($realPath)) . '</article>';
} else {
include $realPath;
}
$content = ob_get_clean();
// Build navigation for templates // Prepare template variables
$navigation = buildNavigation($contentDir, $currentLang, $defaultLang); extract(prepareTemplateContext());
// Load metadata for current page/directory
$pageDir = dirname($realPath); $pageDir = dirname($realPath);
$pageMetadata = loadMetadata($pageDir, $currentLang, $defaultLang); $pageMetadata = loadMetadata($pageDir, $currentLang, $defaultLang);
$pageTitle = $pageMetadata['title'] ?? null; $pageTitle = $pageMetadata['title'] ?? null;
// Load frontpage metadata for home button label
$frontpageMetadata = loadMetadata($contentDir, $currentLang, $defaultLang);
$homeLabel = $frontpageMetadata['slug'] ?? 'Home';
// Load translations
$translations = loadTranslations($currentLang);
// Wrap content with page template // Wrap content with page template
ob_start(); ob_start();
include $pageTemplate; include $pageTemplate;
@ -83,38 +83,14 @@ function renderMultipleFiles(array $filePaths, string $pageDir): void {
} }
// Render all content files in order // Render all content files in order
$content = ''; $content = implode('', array_map('renderContentFile', $filePaths));
foreach ($filePaths as $filePath) {
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
ob_start();
if ($ext === 'md') {
if (!class_exists('Parsedown')) {
require_once __DIR__ . '/vendor/Parsedown.php';
}
echo '<article>' . (new Parsedown())->text(file_get_contents($filePath)) . '</article>';
} elseif ($ext === 'html') {
include $filePath;
} elseif ($ext === 'php') {
include $filePath;
}
$content .= ob_get_clean();
}
// Build navigation for templates // Prepare template variables
$navigation = buildNavigation($contentDir, $currentLang, $defaultLang); extract(prepareTemplateContext());
// Load metadata for current page/directory
$pageMetadata = loadMetadata($pageDir, $currentLang, $defaultLang); $pageMetadata = loadMetadata($pageDir, $currentLang, $defaultLang);
$pageTitle = $pageMetadata['title'] ?? null; $pageTitle = $pageMetadata['title'] ?? null;
// Load frontpage metadata for home button label
$frontpageMetadata = loadMetadata($contentDir, $currentLang, $defaultLang);
$homeLabel = $frontpageMetadata['slug'] ?? 'Home';
// Load translations
$translations = loadTranslations($currentLang);
// Wrap content with page template // Wrap content with page template
ob_start(); ob_start();
include $pageTemplate; include $pageTemplate;

View file

@ -1,8 +1,9 @@
<?php <?php
// Load modular components // Load modular components
require_once __DIR__ . '/config.php'; require_once __DIR__ . '/constants.php';
require_once __DIR__ . '/helpers.php'; require_once __DIR__ . '/helpers.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';
@ -16,9 +17,7 @@ if (file_exists($assetPath) && is_file($assetPath)) {
// Handle frontpage // Handle frontpage
if (empty($requestPath)) { if (empty($requestPath)) {
// Find all content files in the root content directory
$contentFiles = findAllContentFiles($contentDir, $currentLang, $defaultLang); $contentFiles = findAllContentFiles($contentDir, $currentLang, $defaultLang);
if (!empty($contentFiles)) { if (!empty($contentFiles)) {
renderMultipleFiles($contentFiles, $contentDir); renderMultipleFiles($contentFiles, $contentDir);
} }
@ -56,19 +55,7 @@ switch ($parsedPath['type']) {
$pageContent = null; $pageContent = null;
$contentFiles = findAllContentFiles($dir, $currentLang, $defaultLang); $contentFiles = findAllContentFiles($dir, $currentLang, $defaultLang);
if (!empty($contentFiles)) { if (!empty($contentFiles)) {
ob_start(); $pageContent = implode('', array_map('renderContentFile', $contentFiles));
foreach ($contentFiles as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext === 'md') {
if (!class_exists('Parsedown')) {
require_once __DIR__ . '/vendor/Parsedown.php';
}
echo (new Parsedown())->text(file_get_contents($file));
} else {
include $file;
}
}
$pageContent = ob_get_clean();
} }
// Load metadata for this directory // Load metadata for this directory
@ -79,10 +66,10 @@ switch ($parsedPath['type']) {
$templateName = $metadata['page_template']; $templateName = $metadata['page_template'];
// Add .php extension if not present // Add .php extension if not present
if (!str_ends_with($templateName, '.php')) { if (!str_ends_with($templateName, '.php')) {
$templateName .= '.php'; $templateName = str_replace('.php', '', $templateName);
} }
$customTemplate = dirname(__DIR__) . '/custom/templates/' . $templateName; $customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php";
$defaultTemplate = __DIR__ . '/default/templates/' . $templateName; $defaultTemplate = __DIR__ . "/default/templates/$templateName.php";
if (file_exists($customTemplate)) { if (file_exists($customTemplate)) {
$listTemplate = $customTemplate; $listTemplate = $customTemplate;
@ -92,13 +79,11 @@ switch ($parsedPath['type']) {
// If template doesn't exist, fall back to default $listTemplate // If template doesn't exist, fall back to default $listTemplate
} }
// Default directory listing // Build list items
$subdirs = array_filter( $subdirs = getSubdirectories($dir);
scandir($dir) ?: [], $langPrefix = getLangPrefix($currentLang, $defaultLang);
fn($item) => !in_array($item, ['.', '..']) && is_dir("$dir/$item")
);
$items = array_filter(array_map(function($item) use ($dir, $requestPath, $currentLang, $defaultLang) { $items = array_filter(array_map(function($item) use ($dir, $requestPath, $currentLang, $defaultLang, $langPrefix) {
$itemPath = "$dir/$item"; $itemPath = "$dir/$item";
// Check if content exists for current language // Check if content exists for current language
@ -119,20 +104,20 @@ switch ($parsedPath['type']) {
$date = extractDateFromFolder($item) ?: date("F d, Y", filemtime($itemPath)); $date = extractDateFromFolder($item) ?: date("F d, Y", filemtime($itemPath));
} }
$langPrefix = $currentLang !== $defaultLang ? "/$currentLang" : '';
// Use translated slug if available, otherwise use folder name // Use translated slug if available, otherwise use folder name
$urlSlug = ($currentLang !== $defaultLang && $metadata && isset($metadata['slug'])) $urlSlug = ($currentLang !== $defaultLang && $metadata && isset($metadata['slug']))
? $metadata['slug'] ? $metadata['slug']
: $item; : $item;
$baseUrl = $langPrefix . '/' . trim($requestPath, '/') . '/' . urlencode($urlSlug);
return [ return [
'title' => $title, 'title' => $title,
'date' => $date, 'date' => $date,
'url' => $langPrefix . '/' . trim($requestPath, '/') . '/' . urlencode($urlSlug), 'url' => $baseUrl,
'cover' => $coverImage ? $langPrefix . '/' . trim($requestPath, '/') . '/' . urlencode($urlSlug) . '/' . $coverImage : null, 'cover' => $coverImage ? "$baseUrl/$coverImage" : null,
'summary' => $metadata['summary'] ?? null, 'summary' => $metadata['summary'] ?? null,
'pdf' => $pdfFile ? $langPrefix . '/' . trim($requestPath, '/') . '/' . urlencode($urlSlug) . '/' . $pdfFile : null, 'pdf' => $pdfFile ? "$baseUrl/$pdfFile" : null,
'redirect' => $metadata['redirect'] ?? null 'redirect' => $metadata['redirect'] ?? null
]; ];
}, $subdirs)); }, $subdirs));