folderweb/docs/03-reference/03-template-variables.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

11 KiB

Template Variables Reference

Templates have access to a set of variables provided by FolderWeb and its plugins. This reference documents all available variables and their types.

Base Template Variables

Available in base.php:

$content

The fully rendered HTML content from the page or list template.

Type: String (HTML)
Example:

<main>
  <?= $content ?>
</main>

$pageTitle

The page title for the <title> tag.

Type: String
Default: "FolderWeb"
Example:

<title><?= htmlspecialchars($pageTitle ?? 'FolderWeb') ?></title>

Source:

  1. Language-specific metadata [lang] title
  2. Root metadata title
  3. First heading in content
  4. Folder name

$metaDescription

SEO description for the <meta name="description"> tag.

Type: String
Optional: May be empty
Example:

<?php if (!empty($metaDescription)): ?>
  <meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
<?php endif; ?>

Source:

  1. Metadata search_description
  2. Metadata summary
  3. Empty if not set

$socialImageUrl

URL to cover image for social media meta tags.

Type: String (URL)
Optional: May be empty
Example:

<?php if (!empty($socialImageUrl)): ?>
  <meta property="og:image" content="<?= htmlspecialchars($socialImageUrl) ?>">
<?php endif; ?>

Source: First cover.* image found in content directory

$navigation

Array of navigation menu items.

Type: Array of associative arrays
Structure:

[
  ['url' => '/about/', 'title' => 'About'],
  ['url' => '/blog/', 'title' => 'Blog'],
  ['url' => '/contact/', 'title' => 'Contact'],
]

Example:

<?php if (!empty($navigation)): ?>
  <nav>
    <?php foreach ($navigation as $item): ?>
      <a href="<?= htmlspecialchars($item['url']) ?>">
        <?= htmlspecialchars($item['title']) ?>
      </a>
    <?php endforeach; ?>
  </nav>
<?php endif; ?>

Source: Pages with menu = 1 in metadata, sorted by menu_order

$homeLabel

Text for the home link.

Type: String
Default: "Home"
Example:

<a href="/"><?= htmlspecialchars($homeLabel ?? 'Home') ?></a>

Source: Translation string translations['home'] or fallback "Home"

$currentLang

Current language code.

Type: String
Default: From config.ini languages.default
Example:

<html lang="<?= htmlspecialchars($currentLang ?? 'en') ?>">

Values: ISO 639-1 language codes (en, no, de, etc.)

$langPrefix

URL prefix for the current language.

Type: String
Default: Empty string for default language
Example:

<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/">Home</a>

Values:

  • "" (empty) for default language
  • "/no" for Norwegian
  • "/de" for German
  • etc.

$languageUrls

URLs to switch between available languages.

Type: Associative array
Structure:

[
  'en' => '/page/',
  'no' => '/no/side/',
  'de' => '/de/seite/',
]

Example:

<?php if (!empty($languageUrls) && count($languageUrls) > 1): ?>
  <nav class="language-switcher">
    <?php foreach ($languageUrls as $lang => $url): ?>
      <a href="<?= htmlspecialchars($url) ?>" 
         <?= ($lang === $currentLang) ? 'aria-current="true"' : '' ?>>
        <?= htmlspecialchars(strtoupper($lang)) ?>
      </a>
    <?php endforeach; ?>
  </nav>
<?php endif; ?>

$translations

Translated UI strings for the current language.

Type: Associative array
Structure:

[
  'home' => 'Home',
  'footer_handcoded' => 'Generated in',
  'footer_page_time' => 'ms',
  'months' => 'January,February,March,...',
]

Example:

<p><?= htmlspecialchars($translations['home'] ?? 'Home') ?></p>

Source: Language files in custom/languages/[lang].ini or app/default/languages/[lang].ini

$pageCssUrl

URL to page-specific CSS file.

Type: String (URL)
Optional: Only set if styles.css exists in content directory
Example:

<?php if (!empty($pageCssUrl)): ?>
  <link rel="stylesheet" href="<?= htmlspecialchars($pageCssUrl) ?>?v=<?= htmlspecialchars($pageCssHash ?? '') ?>">
<?php endif; ?>

$pageCssHash

MD5 hash of page-specific CSS for cache busting.

Type: String (MD5 hash)
Optional: Only set if $pageCssUrl exists
Example: See $pageCssUrl above

$feedUrl

URL to the Atom feed for the current list page.

Type: String (URL path)
Optional: Only set on list pages with feed = true in metadata
Example:

<?php if (!empty($feedUrl)): ?>
  <link rel="alternate" type="application/atom+xml" title="<?= htmlspecialchars($pageTitle ?? 'Feed') ?>" href="<?= htmlspecialchars($feedUrl) ?>">
<?php endif; ?>

Source: Set when feed = true in the list directory's metadata.ini

Page Template Variables

Available in page.php:

$content

The fully rendered HTML content from the page.

Type: String (HTML)
Example:

<article>
  <?= $content ?>
</article>

$metadata

All metadata for the current page.

Type: Associative array
Structure:

[
  'title' => 'Page Title',
  'summary' => 'Short description',
  'date' => '2024-12-15',
  'show_date' => true,
  'author' => 'Jane Doe',  // Custom fields
  'tags' => 'web,design',
  // ... all other metadata fields
]

Example:

<?php if (isset($metadata['title'])): ?>
  <h1><?= htmlspecialchars($metadata['title']) ?></h1>
<?php endif; ?>

<?php if (isset($metadata['date']) && ($metadata['show_date'] ?? true)): ?>
  <time datetime="<?= $metadata['date'] ?>">
    <?= $metadata['date'] ?>
  </time>
<?php endif; ?>

List Template Variables

Available in list.php, list-grid.php, list-compact.php, etc.:

$pageContent

Optional intro content from the directory's own files.

Type: String (HTML)
Optional: May be empty
Example:

<?php if ($pageContent): ?>
  <div class="intro">
    <?= $pageContent ?>
  </div>
<?php endif; ?>

Source: Content files in the list directory itself (not subdirectories)

$items

Array of items to display in the list.

Type: Array of associative arrays
Structure:

[
  [
    'title' => 'My Post',
    'url' => '/blog/my-post/',
    'date' => '15. desember 2024',       // Formatted for display
    'rawDate' => '2024-12-15',           // ISO YYYY-MM-DD
    'summary' => 'Short description',
    'cover' => '/blog/2024-12-15-my-post/cover.jpg',
    'pdf' => null,
    'redirect' => null,
    'dirPath' => '/path/to/content/blog/2024-12-15-my-post',
  ],
  // ... more items
]

Example:

<?php foreach ($items as $item): ?>
  <article>
    <?php if ($item['cover']): ?>
      <img src="<?= $item['cover'] ?>" 
           alt="<?= htmlspecialchars($item['title']) ?>">
    <?php endif; ?>
    
    <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; ?>

$metadata

Metadata for the list directory itself.

Type: Associative array
Structure: Same as page metadata
Example:

<?php if (isset($metadata['title'])): ?>
  <h1><?= htmlspecialchars($metadata['title']) ?></h1>
<?php endif; ?>

Item Properties

Each item in $items has these properties:

Property Type Description Optional
title String Item title (from metadata, heading, or folder name) No
url String Full URL to the item (with trailing slash + lang prefix) No
date String Formatted date string (plugin-processed for display) Yes
rawDate String ISO date (YYYY-MM-DD) for feeds and <time> elements Yes
summary String Short description from metadata Yes
cover String URL to cover image Yes
pdf String URL to first PDF file Yes
redirect String External redirect URL Yes
dirPath String Filesystem path to item directory (internal use) No

Adding Custom Variables

Use the Hook::TEMPLATE_VARS hook to add custom variables:

Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx) {
    // Add a custom variable
    $vars['siteName'] = 'My Website';
    
    // Add computed values
    $vars['currentYear'] = date('Y');
    
    // Add from context
    $vars['userName'] = $ctx->get('user_name', 'Guest');
    
    return $vars;
});

Then use in templates:

<p>&copy; <?= $currentYear ?> <?= htmlspecialchars($siteName) ?></p>

Variable Availability by Template

Variable base.php page.php list.php
$content
$pageTitle
$metaDescription
$socialImageUrl
$navigation
$homeLabel
$currentLang
$langPrefix
$languageUrls
$translations
$pageCssUrl
$pageCssHash
$feedUrl
$metadata
$pageContent
$items

Note: All variables are technically available everywhere via plugin hooks, but this table shows the default availability.

Escaping Output

Always escape user content to prevent XSS attacks:

<!-- Good -->
<h1><?= htmlspecialchars($metadata['title']) ?></h1>
<p><?= htmlspecialchars($item['summary']) ?></p>

<!-- Bad -->
<h1><?= $metadata['title'] ?></h1>
<p><?= $item['summary'] ?></p>

Exception: Already-sanitized HTML like $content (rendered from Markdown):

<!-- Good (content is already HTML) -->
<div class="content">
  <?= $content ?>
</div>

Checking Variable Existence

Always check if optional variables exist:

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

<!-- Bad (may cause warnings) -->
<p>By <?= htmlspecialchars($metadata['author']) ?></p>

Use null coalescing for defaults:

<p><?= htmlspecialchars($metadata['author'] ?? 'Anonymous') ?></p>

Debugging Variables

To see all available variables in a template:

<pre><?php var_dump(get_defined_vars()); ?></pre>

Or specific variables:

<pre><?php print_r($metadata); ?></pre>
<pre><?php print_r($items); ?></pre>

Remember to remove debug code before deploying to production.

What's Next?