Add Atom feed support to list pages
Introduce `feed` metadata option to enable Atom feeds Update list item structure with standardized fields Add `$feedUrl` template variable for autodiscovery Improve date handling with raw/processed date separation Document feed generation in architecture and rendering docs Update template examples to use new item structure
This commit is contained in:
parent
1cbfb67a4c
commit
069ce389ea
7 changed files with 128 additions and 48 deletions
|
|
@ -76,22 +76,29 @@ router.php
|
|||
├─ 3. Check custom/assets/{path} → serve static file + exit
|
||||
├─ 4. Check content/{path} for static asset (css/img/pdf/font) → serve + exit
|
||||
│
|
||||
├─ 5. Empty path? → frontpage: findAllContentFiles + renderMultipleFiles
|
||||
├─ 5. Path ends with feed.xml? → Atom feed generation
|
||||
│ ├─ Strip feed.xml, resolve parent as list directory
|
||||
│ ├─ Check feed = true in metadata, otherwise 404
|
||||
│ ├─ buildListItems() + renderContentFile() for full content
|
||||
│ └─ Output Atom XML + exit
|
||||
│
|
||||
└─ 6. parseRequestPath() → {type, path}
|
||||
├─ 6. Empty path? → frontpage: findAllContentFiles + renderMultipleFiles
|
||||
│
|
||||
└─ 7. parseRequestPath() → {type, path}
|
||||
│
|
||||
├─ "page": trailing slash redirect → findAllContentFiles → renderMultipleFiles
|
||||
│ (fires Hook::PROCESS_CONTENT for file filtering)
|
||||
│ (fires Hook::TEMPLATE_VARS before template render)
|
||||
│ Template chain: content → page.php → base.php
|
||||
│
|
||||
├─ "list": trailing slash redirect → build items array from subdirectories
|
||||
├─ "list": trailing slash redirect → buildListItems() from helpers.php
|
||||
│ ├─ Check hide_list metadata → treat as page if true
|
||||
│ ├─ Select list template from metadata page_template
|
||||
│ ├─ For each subdir: loadMetadata, extractTitle, extractDateFromFolder, findCoverImage
|
||||
│ ├─ buildListItems(): metadata, titles, dates, covers for each subdir
|
||||
│ ├─ Sort items by date (metadata `order` = ascending|descending)
|
||||
│ ├─ Store pageTitle, metaDescription, feedUrl etc. on context
|
||||
│ ├─ Fire Hook::TEMPLATE_VARS
|
||||
│ └─ Template chain: items → list-*.php → base.php
|
||||
│ └─ Template chain: items → list-*.php → base.php (via renderTemplate)
|
||||
│
|
||||
└─ "not_found": 404 response
|
||||
```
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ Returns flat key-value array with a special `_raw` key containing the full parse
|
|||
| `menu_order` | int | 999 | Navigation sort order (ascending) |
|
||||
| `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`) |
|
||||
| `plugins` | string | — | Comma-separated page-level plugin names |
|
||||
|
||||
### Settings Section
|
||||
|
|
@ -83,7 +84,9 @@ Any key not listed above is passed through to templates/plugins unchanged. Add w
|
|||
|
||||
## Date Extraction
|
||||
|
||||
**`extractDateFromFolder(string $folderName): ?string`** — Extracts date from `YYYY-MM-DD-*` prefix.
|
||||
**`extractRawDateFromFolder(string $folderName): ?string`** — Extracts raw `YYYY-MM-DD` string from folder name prefix. Returns null if no date prefix. No hook processing.
|
||||
|
||||
**`extractDateFromFolder(string $folderName): ?string`** — Calls `extractRawDateFromFolder()` then passes the result through `Hook::PROCESS_CONTENT($date, 'date_format')` for plugin formatting (e.g., `"1. January 2025"`).
|
||||
|
||||
If no date prefix exists and no `date` metadata is set, falls back to file modification time (`filemtime`). All dates pass through `Hook::PROCESS_CONTENT($date, 'date_format')` for plugin formatting.
|
||||
|
||||
|
|
@ -91,6 +94,16 @@ If no date prefix exists and no `date` metadata is set, falls back to file modif
|
|||
|
||||
**Sorting with null dates:** Items without any date are sorted as empty strings via `strcmp`. Their relative order among other dateless items is undefined.
|
||||
|
||||
## List Item Building
|
||||
|
||||
**`buildListItems(string $dir, Context $ctx, ?array $parentMetadata): array`** — Builds and sorts the items array for list views. Defined in `helpers.php`.
|
||||
|
||||
For each subdirectory: loads metadata, extracts title/date/cover/PDF, builds URL with lang prefix and slug. Returns sorted array — direction controlled by `order` metadata on parent (`descending` default).
|
||||
|
||||
Each item contains both a formatted `date` (hook-processed for display) and a `rawDate` (ISO `YYYY-MM-DD` for Atom feeds and `<time>` elements). Also includes `dirPath` (filesystem path) used by the feed generator to render full content.
|
||||
|
||||
Used by both the list case in `router.php` and the Atom feed generator.
|
||||
|
||||
## Navigation
|
||||
|
||||
**`buildNavigation(Context $ctx): array`** — Scans top-level content directories.
|
||||
|
|
|
|||
|
|
@ -44,6 +44,18 @@ Also supports magic property access: `$ctx->foo = 'bar'` / `$val = $ctx->foo`.
|
|||
| `langPrefix` | string | languages.php | URL prefix: `""` for default, `"/no"` for others |
|
||||
| `translations` | array | languages.php | Merged translation strings for current language |
|
||||
|
||||
### Built-in Context Keys (set by router.php for list pages)
|
||||
|
||||
These are set in the list case so that `renderTemplate()` can pass them to `base.php`:
|
||||
|
||||
| Key | Type | Description |
|
||||
|---|---|---|
|
||||
| `pageTitle` | ?string | Page title from metadata (for `<title>` tag) |
|
||||
| `metaDescription` | ?string | SEO description |
|
||||
| `pageCssUrl` | ?string | Page-specific CSS URL |
|
||||
| `pageCssHash` | ?string | CSS cache-bust hash |
|
||||
| `feedUrl` | ?string | Atom feed URL (set when `feed = true` in metadata) |
|
||||
|
||||
## Templates Class
|
||||
|
||||
Defined in `app/context.php`. Readonly value object.
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ Variables are injected via `extract()` — each array key becomes a local variab
|
|||
| `$homeLabel` | string | yes | Home link text |
|
||||
| `$pageCssUrl` | ?string | no | Page-specific CSS URL |
|
||||
| `$pageCssHash` | ?string | no | CSS cache-bust hash |
|
||||
| `$feedUrl` | ?string | no | Atom feed URL (only on lists with `feed = true`) |
|
||||
| `$currentLang` | string | plugin | Language code (from languages plugin) |
|
||||
| `$langPrefix` | string | plugin | URL language prefix |
|
||||
| `$languageUrls` | array | plugin | `[lang => url]` for language switcher |
|
||||
|
|
@ -92,10 +93,12 @@ Each entry in `$items`:
|
|||
| `title` | string | yes | From metadata, first heading, or folder name |
|
||||
| `url` | string | yes | Full URL path with trailing slash and lang prefix |
|
||||
| `date` | ?string | no | Formatted date string (plugin-processed) |
|
||||
| `rawDate` | ?string | no | ISO `YYYY-MM-DD` date (for feeds, `<time>` elements) |
|
||||
| `summary` | ?string | no | From metadata |
|
||||
| `cover` | ?string | no | URL to cover image |
|
||||
| `pdf` | ?string | no | URL to first PDF file |
|
||||
| `redirect` | ?string | no | External redirect URL |
|
||||
| `dirPath` | string | yes | Filesystem path to item directory (internal use) |
|
||||
|
||||
Items sorted by date — direction controlled by `order` metadata on parent (`descending` default, `ascending` available).
|
||||
|
||||
|
|
|
|||
|
|
@ -36,13 +36,26 @@ Handled directly in `router.php` (not a separate function):
|
|||
1. Render directory's own content files as `$pageContent`
|
||||
2. Load metadata, check `hide_list`
|
||||
3. Select list template from `page_template` metadata
|
||||
4. Build `$items` array from subdirectories (metadata + extraction)
|
||||
5. Sort items by date
|
||||
4. Call `buildListItems()` from `helpers.php` (builds + sorts items)
|
||||
5. Store `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, `feedUrl` on context
|
||||
6. Fire `Hook::TEMPLATE_VARS`
|
||||
7. Render list template → capture as `$content`
|
||||
8. Pass to `renderTemplate()` which renders `base.php`
|
||||
|
||||
**`renderTemplate(Context $ctx, string $content, int $statusCode = 200): void`** — Wraps content in base template. Used for list views and error pages.
|
||||
**`renderTemplate(Context $ctx, string $content, int $statusCode = 200): void`** — Wraps content in base template. Used for list views and error pages. Reads `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, and `feedUrl` from the context object (set by the list case in `router.php`). For error pages, these context keys are unset, so base.php receives nulls.
|
||||
|
||||
## Atom Feed Rendering
|
||||
|
||||
Handled in `router.php` before `parseRequestPath()`. When a request path ends with `feed.xml`:
|
||||
|
||||
1. Strip `feed.xml` suffix, resolve parent as list directory via `parseRequestPath()`
|
||||
2. Check `feed = true` in metadata — 404 if missing or if parent is not a list
|
||||
3. Call `buildListItems()` to get items
|
||||
4. For each item: call `findAllContentFiles()` + `renderContentFile()` to get full HTML content
|
||||
5. Build Atom XML with absolute URLs (`$_SERVER['HTTP_HOST']` + scheme detection)
|
||||
6. Output `Content-Type: application/atom+xml` and exit
|
||||
|
||||
Feed piggybacks on the existing Markdown cache — no separate feed cache needed. The `rawDate` field on items provides ISO dates for Atom `<updated>` elements. Content is wrapped in `<![CDATA[...]]>` with `]]>` safely escaped.
|
||||
|
||||
## Markdown Caching
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue