Add support for page-specific JavaScript files with cache busting via MD5 hash. The script is loaded at the end of the body with defer attribute. The JavaScript file must be named script.js and located in the same directory as the page content.
187 lines
6.1 KiB
PHP
187 lines
6.1 KiB
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 extractTitle(string $filePath): ?string {
|
|
$files = findAllContentFiles($filePath);
|
|
if (empty($files)) return null;
|
|
|
|
// Check the first content file for a title
|
|
$file = $files[0];
|
|
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
|
$content = file_get_contents($file);
|
|
|
|
if ($ext === 'md' && preg_match('/^#\s+(.+)$/m', $content, $matches)) {
|
|
return trim($matches[1]);
|
|
}
|
|
if (in_array($ext, ['html', 'php']) && preg_match('/<h1[^>]*>(.*?)<\/h1>/i', $content, $matches)) {
|
|
return strip_tags($matches[1]);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Build sorted list items for a directory
|
|
function buildListItems(string $dir, Context $ctx, ?array $parentMetadata): array {
|
|
$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;
|
|
|
|
$rawDate = null;
|
|
$date = null;
|
|
if (isset($metadata['date'])) {
|
|
$rawDate = $metadata['date'];
|
|
$date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format');
|
|
} else {
|
|
$rawDate = extractRawDateFromFolder($item);
|
|
if ($rawDate) {
|
|
$date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format');
|
|
} else {
|
|
$rawDate = date("Y-m-d", filemtime($itemPath));
|
|
$date = Hooks::apply(Hook::PROCESS_CONTENT, $rawDate, 'date_format');
|
|
}
|
|
}
|
|
|
|
$urlSlug = ($metadata && isset($metadata['slug'])) ? $metadata['slug'] : $item;
|
|
|
|
$langPrefix = $ctx->get('langPrefix', '');
|
|
$baseUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($urlSlug);
|
|
$assetUrl = $langPrefix . '/' . trim($ctx->requestPath, '/') . '/' . urlencode($item);
|
|
|
|
return [
|
|
'title' => $title,
|
|
'url' => $baseUrl . '/',
|
|
'date' => $date,
|
|
'rawDate' => $rawDate,
|
|
'summary' => $metadata['summary'] ?? null,
|
|
'cover' => $coverImage ? "$assetUrl/$coverImage" : null,
|
|
'pdf' => $pdfFile ? "$assetUrl/$pdfFile" : null,
|
|
'redirect' => $metadata['redirect'] ?? null,
|
|
'dirPath' => $itemPath
|
|
];
|
|
}, $subdirs));
|
|
|
|
$sortOrder = strtolower($parentMetadata['order'] ?? 'descending');
|
|
if ($sortOrder === 'ascending') {
|
|
usort($items, fn($a, $b) => strcmp($a['date'] ?? '', $b['date'] ?? ''));
|
|
} else {
|
|
usort($items, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
function extractRawDateFromFolder(string $folderName): ?string {
|
|
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})-/', $folderName, $matches)) {
|
|
return $matches[1] . '-' . $matches[2] . '-' . $matches[3];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function extractDateFromFolder(string $folderName): ?string {
|
|
$raw = extractRawDateFromFolder($folderName);
|
|
if ($raw) {
|
|
return Hooks::apply(Hook::PROCESS_CONTENT, $raw, 'date_format');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findCoverImage(string $dirPath): ?string {
|
|
$found = array_find(
|
|
COVER_IMAGE_EXTENSIONS,
|
|
fn($ext) => file_exists("$dirPath/cover.$ext")
|
|
);
|
|
return $found ? "cover.$found" : null;
|
|
}
|
|
|
|
function findPdfFile(string $dirPath): ?string {
|
|
$pdfs = glob("$dirPath/*.pdf") ?: [];
|
|
return $pdfs ? basename($pdfs[0]) : null;
|
|
}
|
|
|
|
function findPageCss(string $dirPath, string $contentDir): ?array {
|
|
$cssFile = "$dirPath/styles.css";
|
|
if (!file_exists($cssFile) || !is_file($cssFile)) {
|
|
return null;
|
|
}
|
|
|
|
$relativePath = str_replace($contentDir, '', $dirPath);
|
|
$relativePath = trim($relativePath, '/');
|
|
$cssUrl = '/' . ($relativePath ? $relativePath . '/' : '') . 'styles.css';
|
|
|
|
return [
|
|
'url' => $cssUrl,
|
|
'hash' => hash_file('md5', $cssFile)
|
|
];
|
|
}
|
|
|
|
function findPageJs(string $dirPath, string $contentDir): ?array {
|
|
$jsFile = "$dirPath/script.js";
|
|
if (!file_exists($jsFile) || !is_file($jsFile)) {
|
|
return null;
|
|
}
|
|
|
|
$relativePath = str_replace($contentDir, '', $dirPath);
|
|
$relativePath = trim($relativePath, '/');
|
|
$jsUrl = '/' . ($relativePath ? $relativePath . '/' : '') . 'script.js';
|
|
|
|
return [
|
|
'url' => $jsUrl,
|
|
'hash' => hash_file('md5', $jsFile)
|
|
];
|
|
}
|
|
|
|
function extractMetaDescription(string $dirPath, ?array $metadata): ?string {
|
|
// 1. Check for search_description in metadata
|
|
if ($metadata && isset($metadata['search_description'])) {
|
|
return $metadata['search_description'];
|
|
}
|
|
|
|
// 2. Fall back to summary in metadata
|
|
if ($metadata && isset($metadata['summary'])) {
|
|
return $metadata['summary'];
|
|
}
|
|
|
|
// 3. Fall back to first paragraph in content files
|
|
$files = findAllContentFiles($dirPath);
|
|
if (empty($files)) return null;
|
|
|
|
foreach ($files as $file) {
|
|
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
|
$content = file_get_contents($file);
|
|
|
|
if ($ext === 'md') {
|
|
$lines = explode("\n", $content);
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if (empty($line) || str_starts_with($line, '#')) continue;
|
|
if (strlen($line) > 20) {
|
|
return strip_tags($line);
|
|
}
|
|
}
|
|
} elseif (in_array($ext, ['html', 'php'])) {
|
|
if (preg_match('/<p[^>]*>(.*?)<\/p>/is', $content, $matches)) {
|
|
return strip_tags($matches[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|