folderweb/docs/03-reference/02-metadata.md
Ruben 069ce389ea 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
2026-02-06 18:24:39 +01:00

9.5 KiB

Metadata Reference

Metadata files (metadata.ini) configure individual pages and directories. They control titles, URLs, templates, navigation, and more.

Basic Structure

title = "Page Title"
summary = "Short description"
date = "2024-12-15"
search_description = "SEO-friendly description"

Place metadata.ini in any content directory:

content/blog/my-post/
├── index.md
└── metadata.ini

Core Fields

title

The page or item title.

title = "My Blog Post"

Default: Extracted from first # Heading in Markdown, or folder name Used in: Page <title>, list items, navigation menu Type: String

summary

Short description shown in list views.

summary = "A brief introduction to this topic"

Default: None (empty) Used in: List item previews, RSS feeds, social media cards Type: String Recommended length: 150-200 characters

date

Publication or modification date.

date = "2024-12-15"

Default: Extracted from folder name (YYYY-MM-DD-title) Format: YYYY-MM-DD (ISO 8601) Used in: List sorting, date displays, <time> elements Type: String (date)

search_description

SEO meta description for search engines.

search_description = "Learn how to build fast, maintainable websites with FolderWeb"

Default: Uses summary if not set Used in: <meta name="description"> tag Type: String Recommended length: 150-160 characters

slug

Custom URL slug (overrides folder name).

slug = "custom-url"

Default: Folder name (with date prefix removed) Used in: URL generation Type: String (alphanumeric, hyphens, underscores)

Example:

Folder: content/blog/2024-12-15-very-long-title/
Slug: short-title
URL: /blog/short-title/

menu

Show this page in the navigation menu.

menu = 1

Default: 0 (not in menu) Values: 1 (show) or 0 (hide) Type: Integer

menu_order

Position in navigation menu (lower numbers first).

menu = 1
menu_order = 10

Default: 999 (last) Used in: Navigation sorting Type: Integer

Example:

# Home
menu = 1
menu_order = 1

# About
menu = 1
menu_order = 10

# Blog
menu = 1
menu_order = 20

# Contact
menu = 1
menu_order = 30

Settings Section

Advanced settings go in a [settings] section:

title = "My Page"

[settings]
page_template = "list-grid"
show_date = true
hide_list = false

page_template

Which template to use for list views.

[settings]
page_template = "list-grid"

Default: list (uses list.php) Available templates:

  • list — Simple vertical list
  • list-grid — Card grid layout
  • list-compact — Minimal compact list
  • Custom templates you create

Used in: List view rendering Type: String (template name without .php)

show_date

Display the date on the page.

[settings]
show_date = true

Default: true Values: true or false Type: Boolean

hide_list

Don't show list view even if directory has subdirectories.

[settings]
hide_list = true

Default: false Values: true or false Type: Boolean Use case: Section landing pages that should show content instead of list

feed

Enable an Atom feed for this list page, served at /{list-path}/feed.xml.

feed = true

Default: false (no feed generated) Values: true or false Type: Boolean Applies to: List pages only (directories with subdirectories) Effect: Generates an Atom XML feed containing the full rendered content of each list item. Also adds an autodiscovery <link> tag in the HTML <head>.

Language-Specific Overrides

Add language-specific sections to override fields:

title = "About Us"
summary = "Learn about our company"
slug = "about"

[no]
title = "Om oss"
summary = "Les om bedriften vår"
slug = "om"

[de]
title = "Über uns"
summary = "Erfahren Sie mehr über unser Unternehmen"
slug = "uber-uns"

Supported fields in language sections:

  • title
  • summary
  • search_description
  • slug

Language codes: Must match your configured languages (config.ini).

URLs with language-specific slugs:

  • English: /about/
  • Norwegian: /no/om/
  • German: /de/uber-uns/

Custom Fields

Add any custom fields you need:

title = "Project X"
author = "Jane Doe"
client = "ACME Corp"
project_url = "https://example.com"
featured = true
tags = "web,design,portfolio"

Access custom fields in templates:

<?php if (isset($metadata['author'])): ?>
  <p>By <?= htmlspecialchars($metadata['author']) ?></p>
<?php endif; ?>

<?php if (isset($metadata['project_url'])): ?>
  <a href="<?= htmlspecialchars($metadata['project_url']) ?>">
    View Project
  </a>
<?php endif; ?>

Or in plugins:

Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx) {
    $metadata = $ctx->get('metadata', []);
    
    if (isset($metadata['tags'])) {
        $vars['tags'] = explode(',', $metadata['tags']);
    }
    
    return $vars;
});

Complete Example

content/blog/2024-12-15-building-fast-sites/metadata.ini:

# Core fields
title = "Building Fast Websites"
summary = "Learn how to optimize your site for speed and performance"
date = "2024-12-15"
search_description = "A comprehensive guide to building fast, performant websites in 2024"

# Navigation
menu = 0                # Don't show in main menu
menu_order = 999

# Custom slug
slug = "fast-sites"

# Settings
[settings]
show_date = true

# Custom fields
author = "Jane Doe"
category = "Web Performance"
tags = "performance,optimization,web"
estimated_reading_time = 8

# Norwegian translation
[no]
title = "Bygge raske nettsider"
summary = "Lær hvordan du optimaliserer nettstedet ditt for hastighet og ytelse"
search_description = "En omfattende guide til å bygge raske, effektive nettsteder i 2024"
slug = "raske-sider"

Metadata Priority

When determining values, FolderWeb follows this priority:

  1. Language-specific metadata (e.g., [no] section)
  2. Root metadata (e.g., title = "...")
  3. Auto-extracted values (e.g., first heading, folder date)
  4. Defaults (e.g., folder name)

Example:

Folder: content/blog/2024-12-15-my-post/

Title resolution:

  1. Check metadata.ini for [en] title = "..."
  2. Check metadata.ini for title = "..."
  3. Extract from first # Heading in Markdown
  4. Use folder name: "my-post"

Metadata in List Items

When rendering list views, each item in the $items array has these keys:

$item = [
    'title' => 'My Post',                        // From metadata, heading, or folder name
    'url' => '/blog/my-post/',                    // Full URL with trailing slash + lang prefix
    'date' => '15. desember 2024',                // Formatted for display (plugin-processed)
    'rawDate' => '2024-12-15',                    // ISO YYYY-MM-DD (for feeds, <time> elements)
    'summary' => 'Short description',             // From metadata (nullable)
    'cover' => '/blog/2024-12-15-my-post/cover.jpg',  // Cover image URL (nullable)
    'pdf' => '/blog/2024-12-15-my-post/doc.pdf',      // First PDF URL (nullable)
    'redirect' => null,                           // External redirect URL (nullable)
    'dirPath' => '/path/to/content/blog/2024-12-15-my-post',  // Filesystem path (internal)
];

Access in list templates:

<?php foreach ($items as $item): ?>
  <article>
    <h2>
      <a href="<?= $item['url'] ?>">
        <?= htmlspecialchars($item['title']) ?>
      </a>
    </h2>
    
    <?php if ($item['date']): ?>
      <time datetime="<?= $item['rawDate'] ?>"><?= $item['date'] ?></time>
    <?php endif; ?>
    
    <?php if ($item['summary']): ?>
      <p><?= htmlspecialchars($item['summary']) ?></p>
    <?php endif; ?>
  </article>
<?php endforeach; ?>

Best Practices

1. Use Consistent Field Names

# Good: consistent naming
author = "Jane Doe"
published_date = "2024-12-15"
featured = true

# Bad: inconsistent naming  
Author = "Jane Doe"
PublishDate = "2024-12-15"
is_featured = true

2. Keep Summaries Concise

# Good
summary = "Learn to optimize website performance in 5 steps"

# Too long
summary = "This comprehensive article will teach you everything you need to know about optimizing website performance, including lazy loading, code splitting, image optimization, and much more in detailed steps with examples"

3. Use Semantic Custom Fields

# Good: clear purpose
author = "Jane Doe"
category = "Tutorial"
difficulty = "Beginner"

# Bad: unclear purpose
field1 = "Jane Doe"
field2 = "Tutorial"
field3 = "Beginner"

4. Add Comments

# SEO and social media
title = "Building Fast Websites"
search_description = "A guide to web performance optimization"

# Author and categorization
author = "Jane Doe"
category = "Performance"

# Custom display options
featured = true        # Show in featured section
priority = 10          # Higher = more prominent

Debugging Metadata

To see parsed metadata, create a debug template:

custom/templates/page.php:

<pre><?php print_r($metadata); ?></pre>
<hr>
<?= $content ?>

Or in list templates:

<pre><?php print_r($items); ?></pre>
<hr>
<?= $pageContent ?>

Remember to revert before deploying to production.

What's Next?