267 lines
10 KiB
PHP
267 lines
10 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',
|
|
'js' => 'application/javascript',
|
|
];
|
|
|
|
$extLower = strtolower($ext);
|
|
if (isset($mimeTypes[$extLower])) {
|
|
header('Content-Type: ' . $mimeTypes[$extLower]);
|
|
readfile($contentAssetPath);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Handle Atom feed requests
|
|
if (str_ends_with($ctx->requestPath, 'feed.xml')) {
|
|
$feedPath = preg_replace('#/?feed\.xml$#', '', $ctx->requestPath);
|
|
|
|
// Temporarily set requestPath to the parent directory for resolution
|
|
$reflection = new ReflectionProperty($ctx, 'requestPath');
|
|
$originalPath = $ctx->requestPath;
|
|
$reflection->setValue($ctx, $feedPath);
|
|
|
|
$parsedFeed = parseRequestPath($ctx);
|
|
|
|
if ($parsedFeed['type'] !== 'list') {
|
|
$reflection->setValue($ctx, $originalPath);
|
|
} else {
|
|
$dir = $parsedFeed['path'];
|
|
$metadata = loadMetadata($dir);
|
|
|
|
if (!isset($metadata['feed']) || !$metadata['feed']) {
|
|
$reflection->setValue($ctx, $originalPath);
|
|
} else {
|
|
$items = buildListItems($dir, $ctx, $metadata);
|
|
|
|
// Render full content for each item
|
|
foreach ($items as &$item) {
|
|
$item['content'] = '';
|
|
$contentFiles = findAllContentFiles($item['dirPath']);
|
|
foreach ($contentFiles as $file) {
|
|
$item['content'] .= renderContentFile($file, $ctx);
|
|
}
|
|
}
|
|
unset($item);
|
|
|
|
// Build Atom XML
|
|
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
|
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
|
$baseUrl = "$scheme://$host";
|
|
$langPrefix = $ctx->get('langPrefix', '');
|
|
$listUrl = $baseUrl . $langPrefix . '/' . trim($feedPath, '/') . '/';
|
|
$feedUrl = $baseUrl . $langPrefix . '/' . trim($feedPath, '/') . '/feed.xml';
|
|
$feedTitle = $metadata['title'] ?? 'Feed';
|
|
$updated = !empty($items) ? ($items[0]['rawDate'] ?? date('Y-m-d')) : date('Y-m-d');
|
|
|
|
header('Content-Type: application/atom+xml; charset=utf-8');
|
|
echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
|
|
echo '<feed xmlns="http://www.w3.org/2005/Atom">' . "\n";
|
|
echo ' <title>' . htmlspecialchars($feedTitle) . '</title>' . "\n";
|
|
echo ' <link href="' . htmlspecialchars($listUrl) . '" rel="alternate"/>' . "\n";
|
|
echo ' <link href="' . htmlspecialchars($feedUrl) . '" rel="self"/>' . "\n";
|
|
echo ' <id>' . htmlspecialchars($listUrl) . '</id>' . "\n";
|
|
echo ' <updated>' . $updated . 'T00:00:00Z</updated>' . "\n";
|
|
$feedAuthor = $metadata['author'] ?? $feedTitle;
|
|
echo ' <author><name>' . htmlspecialchars($feedAuthor) . '</name></author>' . "\n";
|
|
|
|
$dateCounts = [];
|
|
foreach ($items as $item) {
|
|
$absoluteUrl = $baseUrl . $item['url'];
|
|
$rawDate = $item['rawDate'] ?? date('Y-m-d');
|
|
$dateIndex = $dateCounts[$rawDate] ?? 0;
|
|
$dateCounts[$rawDate] = $dateIndex + 1;
|
|
$itemDate = $rawDate . 'T00:00:' . sprintf('%02d', $dateIndex) . 'Z';
|
|
|
|
echo ' <entry>' . "\n";
|
|
echo ' <title>' . htmlspecialchars($item['title']) . '</title>' . "\n";
|
|
echo ' <link href="' . htmlspecialchars($absoluteUrl) . '" rel="alternate"/>' . "\n";
|
|
echo ' <id>' . htmlspecialchars($absoluteUrl) . '</id>' . "\n";
|
|
echo ' <updated>' . $itemDate . '</updated>' . "\n";
|
|
if ($item['summary']) {
|
|
echo ' <summary>' . htmlspecialchars($item['summary']) . '</summary>' . "\n";
|
|
}
|
|
if ($item['content']) {
|
|
$safeContent = str_replace(']]>', ']]]]><![CDATA[>', $item['content']);
|
|
echo ' <content type="html"><![CDATA[' . $safeContent . ']]></content>' . "\n";
|
|
}
|
|
echo ' </entry>' . "\n";
|
|
}
|
|
|
|
echo '</feed>' . "\n";
|
|
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'];
|
|
|
|
// Redirect to add trailing slash if needed
|
|
if (!$ctx->hasTrailingSlash) {
|
|
header('Location: ' . rtrim($_SERVER['REQUEST_URI'], '/') . '/', true, 301);
|
|
exit;
|
|
}
|
|
|
|
// 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'];
|
|
$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
|
|
$items = buildListItems($dir, $ctx, $metadata);
|
|
|
|
// 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 and JS
|
|
$pageCss = findPageCss($dir, $ctx->contentDir);
|
|
$pageCssUrl = $pageCss['url'] ?? null;
|
|
$pageCssHash = $pageCss['hash'] ?? null;
|
|
|
|
$pageJs = findPageJs($dir, $ctx->contentDir);
|
|
$pageJsUrl = $pageJs['url'] ?? null;
|
|
$pageJsHash = $pageJs['hash'] ?? null;
|
|
|
|
// Build feed URL if feed is enabled
|
|
$langPrefix = $ctx->get('langPrefix', '');
|
|
$feedUrl = (isset($metadata['feed']) && $metadata['feed'])
|
|
? $langPrefix . '/' . trim($ctx->requestPath, '/') . '/feed.xml'
|
|
: null;
|
|
|
|
// Store for base template (renderTemplate reads these from context)
|
|
$ctx->set('pageTitle', $pageTitle);
|
|
$ctx->set('metaDescription', $metaDescription);
|
|
$ctx->set('pageCssUrl', $pageCssUrl);
|
|
$ctx->set('pageCssHash', $pageCssHash);
|
|
$ctx->set('pageJsUrl', $pageJsUrl);
|
|
$ctx->set('pageJsHash', $pageJsHash);
|
|
$ctx->set('feedUrl', $feedUrl);
|
|
|
|
// Let plugins add template variables
|
|
$templateVars = Hooks::apply(Hook::TEMPLATE_VARS, [
|
|
'navigation' => $navigation,
|
|
'homeLabel' => $homeLabel,
|
|
'pageTitle' => $pageTitle,
|
|
'metaDescription' => $metaDescription,
|
|
'pageCssUrl' => $pageCssUrl,
|
|
'pageCssHash' => $pageCssHash,
|
|
'pageJsUrl' => $pageJsUrl,
|
|
'pageJsHash' => $pageJsHash,
|
|
'items' => $items,
|
|
'pageContent' => $pageContent,
|
|
'feedUrl' => $feedUrl
|
|
], $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;
|
|
}
|