From 611bf755761e4893425f23f45413b79a28a357ea Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 17 Mar 2026 11:23:07 +0100 Subject: [PATCH 1/2] Add social image URL and metadata to template context --- app/plugins/global/languages.php | 5 +---- app/rendering.php | 14 ++++++++------ app/router.php | 13 ++++++++++++- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/plugins/global/languages.php b/app/plugins/global/languages.php index 4425454..22a2000 100644 --- a/app/plugins/global/languages.php +++ b/app/plugins/global/languages.php @@ -46,10 +46,7 @@ Hooks::add(Hook::PROCESS_CONTENT, function(mixed $data, string $dirOrType, strin // Filter content files by language variant if (is_array($data) && !empty($data) && isset($data[0]['path'])) { - error_log("filterFilesByLanguage called with " . count($data) . " files, current lang: $currentLang"); - $filtered = filterFilesByLanguage($data, $dirOrType, $ctx); - error_log("Filtered to " . count($filtered) . " files"); - return $filtered; + return filterFilesByLanguage($data, $dirOrType, $ctx); } return $data; diff --git a/app/rendering.php b/app/rendering.php index c4a099a..08dc19e 100644 --- a/app/rendering.php +++ b/app/rendering.php @@ -59,7 +59,8 @@ function renderTemplate(Context $ctx, string $content, int $statusCode = 200): v 'pageCssHash' => $ctx->get('pageCssHash'), 'pageJsUrl' => $ctx->get('pageJsUrl'), 'pageJsHash' => $ctx->get('pageJsHash'), - 'feedUrl' => $ctx->get('feedUrl') + 'feedUrl' => $ctx->get('feedUrl'), + 'socialImageUrl' => $ctx->get('socialImageUrl') ], $ctx); extract($templateVars); @@ -73,8 +74,8 @@ function renderTemplate(Context $ctx, string $content, int $statusCode = 200): v function renderMultipleFiles(Context $ctx, array $files, string $pageDir): void { // Load metadata and page plugins BEFORE rendering content files // so that plugin-provided template variables are available to PHP content files - $pageMetadata = loadMetadata($pageDir); - getPluginManager()->loadPagePlugins($pageMetadata); + $metadata = loadMetadata($pageDir); + getPluginManager()->loadPagePlugins($metadata); $content = ''; foreach ($files as $file) { @@ -83,8 +84,8 @@ function renderMultipleFiles(Context $ctx, array $files, string $pageDir): void $navigation = $ctx->navigation; $homeLabel = $ctx->homeLabel; - $pageTitle = $pageMetadata['title'] ?? null; - $metaDescription = extractMetaDescription($pageDir, $pageMetadata); + $pageTitle = $metadata['title'] ?? null; + $metaDescription = extractMetaDescription($pageDir, $metadata); $pageCss = findPageCss($pageDir, $ctx->contentDir); $pageCssUrl = $pageCss['url'] ?? null; @@ -112,7 +113,8 @@ function renderMultipleFiles(Context $ctx, array $files, string $pageDir): void 'pageCssHash' => $pageCssHash, 'pageJsUrl' => $pageJsUrl, 'pageJsHash' => $pageJsHash, - 'socialImageUrl' => $socialImageUrl + 'socialImageUrl' => $socialImageUrl, + 'metadata' => $metadata ], $ctx); extract($templateVars); diff --git a/app/router.php b/app/router.php index 6f4d3a3..8ad8231 100644 --- a/app/router.php +++ b/app/router.php @@ -249,6 +249,14 @@ switch ($parsedPath['type']) { ? $langPrefix . '/' . trim($ctx->requestPath, '/') . '/feed.xml' : null; + // Build social image URL from cover image if present + $coverImage = findCoverImage($dir); + $socialImageUrl = null; + if ($coverImage) { + $relativePath = trim(str_replace($ctx->contentDir, '', $dir), '/'); + $socialImageUrl = '/' . ($relativePath ? $relativePath . '/' : '') . $coverImage; + } + // Store for base template (renderTemplate reads these from context) $ctx->set('pageTitle', $pageTitle); $ctx->set('metaDescription', $metaDescription); @@ -257,6 +265,7 @@ switch ($parsedPath['type']) { $ctx->set('pageJsUrl', $pageJsUrl); $ctx->set('pageJsHash', $pageJsHash); $ctx->set('feedUrl', $feedUrl); + $ctx->set('socialImageUrl', $socialImageUrl); // Let plugins add template variables $templateVars = Hooks::apply(Hook::TEMPLATE_VARS, [ @@ -270,7 +279,9 @@ switch ($parsedPath['type']) { 'pageJsHash' => $pageJsHash, 'items' => $items, 'pageContent' => $pageContent, - 'feedUrl' => $feedUrl + 'feedUrl' => $feedUrl, + 'socialImageUrl' => $socialImageUrl, + 'metadata' => $metadata ], $ctx); extract($templateVars); From 33943a907b85b6cda0515da8ec68214f4b4b408b Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 17 Mar 2026 11:23:19 +0100 Subject: [PATCH 2/2] Document `author` setting for Atom feeds **Date deduplication:** Atom requires unique `` timestamps per entry. Items sharing the same date get seconds incremented to preserve sort order. **Cache-Control for assets:** JSON/GeoJSON get 60s TTL; others get 1 year immutable. --- docs/04-development/02-content-system.md | 1 + docs/04-development/07-rendering.md | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/04-development/02-content-system.md b/docs/04-development/02-content-system.md index b821dbb..302976c 100644 --- a/docs/04-development/02-content-system.md +++ b/docs/04-development/02-content-system.md @@ -54,6 +54,7 @@ Returns flat key-value array with a special `_raw` key containing the full parse | `order` | string | `"descending"` | List sort direction (`ascending`\|`descending`) | | `redirect` | string | — | External URL (list items can redirect) | | `feed` | bool | `false` | Enable Atom feed on list pages (`feed.xml`) | +| `author` | string | title | Atom feed author name (falls back to page title) | | `plugins` | string | — | Comma-separated page-level plugin names | ### Settings Section diff --git a/docs/04-development/07-rendering.md b/docs/04-development/07-rendering.md index c2d1de1..a761287 100644 --- a/docs/04-development/07-rendering.md +++ b/docs/04-development/07-rendering.md @@ -57,6 +57,8 @@ Handled in `router.php` before `parseRequestPath()`. When a request path ends wi Feed piggybacks on the existing Markdown cache — no separate feed cache needed. The `rawDate` field on items provides ISO dates for Atom `` elements. Content is wrapped in `` with `]]>` safely escaped. +**Date deduplication:** Atom requires unique `` timestamps per entry. Items sharing the same date get seconds incremented (`T00:00:00Z`, `T00:00:01Z`, etc.) to ensure uniqueness while preserving sort order. + ## Markdown Caching Defined in `app/cache.php`. File-based cache in `/tmp/folderweb_cache/`. @@ -87,7 +89,9 @@ Files not in this list are not served as static assets. ### Custom Assets (router.php) -Files in `custom/assets/` are served at the document root URL. Example: `custom/assets/favicon.ico` → `/favicon.ico`. Uses `mime_content_type()` for MIME detection. +Files in `custom/assets/` are served at the document root URL. Example: `custom/assets/favicon.ico` → `/favicon.ico`. Uses an explicit MIME type map; falls back to `application/octet-stream`. + +Cache-Control headers: `json` and `geojson` files get `max-age=60` (short TTL for data files); all other asset types get `max-age=31536000` (1 year, immutable). ### Framework Assets (static.php)