Add publish_date and expiry_date support

This commit is contained in:
Ruben 2026-05-11 20:18:02 +02:00
parent a22281c896
commit 4448798bf5
6 changed files with 148 additions and 3 deletions

View file

@ -121,6 +121,9 @@ function buildNavigation(Context $ctx): array {
$itemPath = "{$ctx->contentDir}/$item"; $itemPath = "{$ctx->contentDir}/$item";
$metadata = loadMetadata($itemPath); $metadata = loadMetadata($itemPath);
// Skip invisible content (future publish or expired)
if (!isVisible($metadata)) continue;
// Only include if explicitly marked as menu item // Only include if explicitly marked as menu item
// parse_ini_file returns boolean true as 1, false as empty string, and "true"/"false" as strings // parse_ini_file returns boolean true as 1, false as empty string, and "true"/"false" as strings
if (!$metadata || !isset($metadata['menu']) || !$metadata['menu']) { if (!$metadata || !isset($metadata['menu']) || !$metadata['menu']) {

View file

@ -1,5 +1,15 @@
<?php <?php
// Check if content is visible based on publish_date and expiry_date
// No metadata or absent fields = always visible
function isVisible(?array $metadata): bool {
if (!$metadata) return true;
$today = gmdate('Y-m-d');
if (isset($metadata['publish_date']) && $today < $metadata['publish_date']) return false;
if (isset($metadata['expiry_date']) && $today > $metadata['expiry_date']) return false;
return true;
}
function resolveTemplate(string $templateName): string { function resolveTemplate(string $templateName): string {
$customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php"; $customTemplate = dirname(__DIR__) . "/custom/templates/$templateName.php";
$defaultTemplate = __DIR__ . "/default/templates/$templateName.php"; $defaultTemplate = __DIR__ . "/default/templates/$templateName.php";
@ -39,6 +49,9 @@ function buildListItems(string $dir, Context $ctx, ?array $parentMetadata): arra
$items = array_filter(array_map(function($item) use ($dir, $ctx) { $items = array_filter(array_map(function($item) use ($dir, $ctx) {
$itemPath = "$dir/$item"; $itemPath = "$dir/$item";
$metadata = loadMetadata($itemPath); $metadata = loadMetadata($itemPath);
if (!isVisible($metadata)) return null;
$coverImage = findCoverImage($itemPath); $coverImage = findCoverImage($itemPath);
$pdfFile = findPdfFile($itemPath); $pdfFile = findPdfFile($itemPath);

View file

@ -194,6 +194,13 @@ switch ($parsedPath['type']) {
exit; exit;
} }
$metadata = loadMetadata($dir);
if (!isVisible($metadata)) {
http_response_code(404);
renderTemplate($ctx, "<h1>404 - Page Not Found</h1><p>The requested page could not be found.</p>", 404);
exit;
}
$contentFiles = findAllContentFiles($dir); $contentFiles = findAllContentFiles($dir);
if (!empty($contentFiles)) { if (!empty($contentFiles)) {
renderMultipleFiles($ctx, $contentFiles, $dir); renderMultipleFiles($ctx, $contentFiles, $dir);
@ -219,6 +226,12 @@ switch ($parsedPath['type']) {
// Load metadata for this directory // Load metadata for this directory
$metadata = loadMetadata($dir); $metadata = loadMetadata($dir);
if (!isVisible($metadata)) {
http_response_code(404);
renderTemplate($ctx, "<h1>404 - Page Not Found</h1><p>The requested page could not be found.</p>", 404);
exit;
}
// Check if hide_list is enabled - if so, treat as page // Check if hide_list is enabled - if so, treat as page
if (isset($metadata['hide_list']) && $metadata['hide_list']) { if (isset($metadata['hide_list']) && $metadata['hide_list']) {
if (!empty($contentFiles)) { if (!empty($contentFiles)) {

View file

@ -0,0 +1,21 @@
--TEST--
isVisible: returns true when metadata is null or date fields absent
--FILE--
<?php
require '/var/www/app/hooks.php';
require '/var/www/app/context.php';
require '/var/www/app/helpers.php';
// Null metadata = always visible
echo isVisible(null) ? "true\n" : "false\n";
// Empty array = always visible
echo isVisible([]) ? "true\n" : "false\n";
// Metadata without date fields = always visible
echo isVisible(['title' => 'Hello']) ? "true\n" : "false\n";
?>
--EXPECT--
true
true
true

View file

@ -0,0 +1,60 @@
--TEST--
isVisible: enforces publish_date and expiry_date boundaries
--FILE--
<?php
require '/var/www/app/hooks.php';
require '/var/www/app/context.php';
require '/var/www/app/helpers.php';
$today = gmdate('Y-m-d');
// Helper to create tomorrow/yesterday dates
$tomorrow = gmdate('Y-m-d', strtotime('+1 day'));
$yesterday = gmdate('Y-m-d', strtotime('-1 day'));
// Future publish_date = hidden
echo isVisible(['publish_date' => $tomorrow]) ? "visible\n" : "hidden\n";
// Past publish_date = visible
echo isVisible(['publish_date' => $yesterday]) ? "visible\n" : "hidden\n";
// Publish date = today = visible (inclusive)
echo isVisible(['publish_date' => $today]) ? "visible\n" : "hidden\n";
// Past expiry_date = hidden
echo isVisible(['expiry_date' => $yesterday]) ? "visible\n" : "hidden\n";
// Future expiry_date = visible
echo isVisible(['expiry_date' => $tomorrow]) ? "visible\n" : "hidden\n";
// Expiry date = today = visible (inclusive)
echo isVisible(['expiry_date' => $today]) ? "visible\n" : "hidden\n";
// Both set, today in range = visible
echo isVisible([
'publish_date' => $yesterday,
'expiry_date' => $tomorrow,
]) ? "visible\n" : "hidden\n";
// Both set, today before range = hidden
echo isVisible([
'publish_date' => $tomorrow,
'expiry_date' => gmdate('Y-m-d', strtotime('+2 days')),
]) ? "visible\n" : "hidden\n";
// Both set, today after range = hidden
echo isVisible([
'publish_date' => gmdate('Y-m-d', strtotime('-3 days')),
'expiry_date' => $yesterday,
]) ? "visible\n" : "hidden\n";
?>
--EXPECT--
hidden
visible
visible
hidden
visible
visible
visible
hidden
hidden

View file

@ -55,9 +55,44 @@ Returns flat key-value array with a special `_raw` key containing the full parse
| `redirect` | string | — | External URL (list items can redirect) | | `redirect` | string | — | External URL (list items can redirect) |
| `feed` | bool | `false` | Enable Atom feed on list pages (`feed.xml`) | | `feed` | bool | `false` | Enable Atom feed on list pages (`feed.xml`) |
| `author` | string | title | Atom feed author name (falls back to page title) | | `author` | string | title | Atom feed author name (falls back to page title) |
| `publish_date` | string (YYYY-MM-DD) | — | Earliest date content is visible |
| `expiry_date` | string (YYYY-MM-DD) | — | Last date content is visible (inclusive) |
| `plugins` | string | — | Comma-separated page-level plugin names | | `plugins` | string | — | Comma-separated page-level plugin names |
### Settings Section ## Visibility (Scheduling)
Content can be scheduled using `publish_date` and `expiry_date`. Both fields are optional — when absent, content is always visible.
When set:
- **`publish_date`** — Content becomes visible on this date (inclusive). Before this date it is treated as non-existent.
- **`expiry_date`** — Content remains visible through this date (inclusive). After this date it is treated as non-existent.
Invisible content is:
- Hidden from list views (`buildListItems()`)
- Excluded from navigation (`buildNavigation()`)
- Returns 404 on direct access (page and list cases in `router.php`)
- Omitted from Atom feeds automatically (feeds use the filtered items array)
The check uses UTC (`gmdate('Y-m-d')`) so behavior is consistent regardless of server timezone.
These fields can be overridden per-language in `[en]` sections via the language plugin:
```ini
title = "Event"
publish_date = "2025-10-01"
expiry_date = "2025-10-07"
[no]
title = "Arrangement"
publish_date = "2025-10-01"
expiry_date = "2025-10-08"
```
### `isVisible(?array $metadata): bool`
Pure function in `helpers.php`. Returns `true` when content should be visible, `false` otherwise. Accepts nullable metadata — returns `true` for `null` (no metadata file = always visible).
## Settings Section
```ini ```ini
[settings] [settings]