Remove redundant quick start section Simplify requirements and installation Clarify local development setup Streamline first page creation Add shared hosting deployment instructions Update tutorial content structure Improve content format explanations Clarify asset handling Simplify metadata documentation Update styling documentation Improve template explanations Remove unnecessary examples Make documentation more concise
289 lines
7.9 KiB
Markdown
289 lines
7.9 KiB
Markdown
# Templates
|
|
|
|
Templates control how content is presented. FolderWeb uses plain PHP templates — HTML with embedded PHP for dynamic output.
|
|
|
|
## Template Types
|
|
|
|
FolderWeb has three template levels:
|
|
|
|
### Base Template (`base.php`)
|
|
|
|
The HTML scaffold wrapping every page. Contains `<head>`, navigation, footer, and a `$content` placeholder for the inner template.
|
|
|
|
### Page Template (`page.php`)
|
|
|
|
Wraps single-page content. Receives the rendered content and metadata.
|
|
|
|
### List Templates (`list.php`, `list-grid.php`, `list-compact.php`)
|
|
|
|
Displays collections of items from subdirectories. Receives an `$items` array and optional intro content.
|
|
|
|
## Template Location
|
|
|
|
Templates live in `custom/templates/`:
|
|
|
|
```
|
|
custom/
|
|
└── templates/
|
|
├── base.php # HTML scaffold
|
|
├── page.php # Single page wrapper
|
|
├── list.php # Default list layout
|
|
├── list-grid.php # Grid card layout
|
|
└── list-compact.php # Compact list layout
|
|
```
|
|
|
|
FolderWeb falls back to `app/default/templates/` for any template not present in `custom/`.
|
|
|
|
## Base Template
|
|
|
|
The base template wraps every page. Here is a minimal example:
|
|
|
|
```php
|
|
<!DOCTYPE html>
|
|
<html lang="<?= $currentLang ?? 'en' ?>">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title><?= htmlspecialchars($pageTitle) ?></title>
|
|
|
|
<?php if (isset($metaDescription)): ?>
|
|
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
|
|
<?php endif; ?>
|
|
|
|
<?php if (isset($socialImageUrl)): ?>
|
|
<meta property="og:image" content="<?= $socialImageUrl ?>">
|
|
<?php endif; ?>
|
|
|
|
<link rel="stylesheet" href="/custom/styles/base.css?v=<?= $cssHash ?? '' ?>">
|
|
|
|
<?php if (isset($pageCssUrl)): ?>
|
|
<link rel="stylesheet" href="<?= $pageCssUrl ?>?v=<?= $pageCssHash ?? '' ?>">
|
|
<?php endif; ?>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<nav>
|
|
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/">My Site</a>
|
|
<ul>
|
|
<?php foreach ($navigation as $item): ?>
|
|
<li>
|
|
<a href="<?= htmlspecialchars($item['url']) ?>">
|
|
<?= htmlspecialchars($item['title']) ?>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
<?= $content ?>
|
|
</main>
|
|
|
|
<footer>
|
|
<p>© <?= date('Y') ?> My Site</p>
|
|
</footer>
|
|
|
|
<?php if (!empty($pageJsUrl)): ?>
|
|
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= $pageJsHash ?? '' ?>"></script>
|
|
<?php endif; ?>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
Key points:
|
|
- `$content` contains the rendered output from the page or list template
|
|
- Always escape user-facing values with `htmlspecialchars()`
|
|
- Check optional variables with `isset()` before using them
|
|
|
|
## Page Template
|
|
|
|
The page template wraps individual page content:
|
|
|
|
```php
|
|
<article>
|
|
<?php if (isset($metadata['title'])): ?>
|
|
<header>
|
|
<h1><?= htmlspecialchars($metadata['title']) ?></h1>
|
|
|
|
<?php if (isset($metadata['date']) && ($metadata['show_date'] ?? true)): ?>
|
|
<time datetime="<?= $metadata['date'] ?>">
|
|
<?= $metadata['formatted_date'] ?>
|
|
</time>
|
|
<?php endif; ?>
|
|
</header>
|
|
<?php endif; ?>
|
|
|
|
<div class="content">
|
|
<?= $content ?>
|
|
</div>
|
|
</article>
|
|
```
|
|
|
|
## List Template
|
|
|
|
List templates display items from subdirectories:
|
|
|
|
```php
|
|
<?php if ($pageContent): ?>
|
|
<div class="page-intro">
|
|
<?= $pageContent ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="blog-list">
|
|
<?php foreach ($items as $item): ?>
|
|
<article>
|
|
<?php if (isset($item['cover_image'])): ?>
|
|
<a href="<?= $item['url'] ?>">
|
|
<img src="<?= $item['cover_image'] ?>"
|
|
alt="<?= htmlspecialchars($item['title']) ?>"
|
|
loading="lazy">
|
|
</a>
|
|
<?php endif; ?>
|
|
|
|
<h2>
|
|
<a href="<?= $item['url'] ?>">
|
|
<?= htmlspecialchars($item['title']) ?>
|
|
</a>
|
|
</h2>
|
|
|
|
<?php if (isset($item['date'])): ?>
|
|
<time datetime="<?= $item['date'] ?>">
|
|
<?= $item['formatted_date'] ?? $item['date'] ?>
|
|
</time>
|
|
<?php endif; ?>
|
|
|
|
<?php if (isset($item['summary'])): ?>
|
|
<p><?= htmlspecialchars($item['summary']) ?></p>
|
|
<?php endif; ?>
|
|
</article>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
```
|
|
|
|
## Choosing a List Template
|
|
|
|
Select which list template to use per directory in `metadata.ini`:
|
|
|
|
```ini
|
|
title = "Projects"
|
|
|
|
[settings]
|
|
page_template = "list-grid"
|
|
```
|
|
|
|
Built-in options:
|
|
- `list` — vertical list (default)
|
|
- `list-grid` — card grid
|
|
- `list-compact` — minimal compact list
|
|
|
|
## Creating Custom List Templates
|
|
|
|
Create a new file in `custom/templates/`. For example, a timeline layout:
|
|
|
|
**custom/templates/list-timeline.php:**
|
|
|
|
```php
|
|
<?= $pageContent ?>
|
|
|
|
<div class="timeline">
|
|
<?php
|
|
$currentYear = null;
|
|
foreach ($items as $item):
|
|
$year = isset($item['date']) ? date('Y', strtotime($item['date'])) : null;
|
|
if ($year && $year !== $currentYear):
|
|
$currentYear = $year;
|
|
?>
|
|
<div class="year-marker">
|
|
<h3><?= $year ?></h3>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<article class="timeline-item">
|
|
<time><?= $item['formatted_date'] ?? '' ?></time>
|
|
<h4><a href="<?= $item['url'] ?>"><?= htmlspecialchars($item['title']) ?></a></h4>
|
|
<?php if (isset($item['summary'])): ?>
|
|
<p><?= htmlspecialchars($item['summary']) ?></p>
|
|
<?php endif; ?>
|
|
</article>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
```
|
|
|
|
Use it with:
|
|
|
|
```ini
|
|
[settings]
|
|
page_template = "list-timeline"
|
|
```
|
|
|
|
## Template Variables
|
|
|
|
### Base template
|
|
|
|
| Variable | Type | Description |
|
|
|---|---|---|
|
|
| `$content` | string | Rendered page/list HTML |
|
|
| `$pageTitle` | string | Page title for `<title>` |
|
|
| `$metaDescription` | string | SEO description |
|
|
| `$navigation` | array | Menu items (`title`, `url`, `order`) |
|
|
| `$homeLabel` | string | Translated "Home" text |
|
|
| `$currentLang` | string | Current language code |
|
|
| `$langPrefix` | string | URL language prefix (`""` or `"/no"`) |
|
|
| `$languageUrls` | array | Links to other language versions |
|
|
| `$translations` | array | Translated UI strings |
|
|
| `$cssHash` | string | Cache-busting hash for base CSS |
|
|
| `$pageCssUrl` | string | Page-specific CSS URL (if exists) |
|
|
| `$pageJsUrl` | string | Page-specific JS URL (if exists) |
|
|
| `$pageLoadTime` | float | Page generation time |
|
|
|
|
### Page template
|
|
|
|
| Variable | Type | Description |
|
|
|---|---|---|
|
|
| `$content` | string | Rendered HTML from content files |
|
|
| `$metadata` | array | Page metadata (title, date, etc.) |
|
|
|
|
### List template
|
|
|
|
| Variable | Type | Description |
|
|
|---|---|---|
|
|
| `$items` | array | Items to display |
|
|
| `$pageContent` | string | Rendered intro content |
|
|
| `$metadata` | array | Directory metadata |
|
|
|
|
Each item in `$items`:
|
|
|
|
| Key | Type | Description |
|
|
|---|---|---|
|
|
| `url` | string | Full URL to item |
|
|
| `title` | string | Item title |
|
|
| `summary` | string | Short description |
|
|
| `date` | string | ISO date (YYYY-MM-DD) |
|
|
| `formatted_date` | string | Localized date string |
|
|
| `cover_image` | string | Cover image URL |
|
|
|
|
See the [Template Variables Reference](../03-reference/03-template-variables.md) for the complete list.
|
|
|
|
## Best Practices
|
|
|
|
**Escape output.** Always use `htmlspecialchars()` for user-facing values. The `$content` variable is already rendered HTML and does not need escaping.
|
|
|
|
**Check variables.** Use `isset()` before accessing optional values:
|
|
|
|
```php
|
|
<?php if (isset($metadata['summary'])): ?>
|
|
<p><?= htmlspecialchars($metadata['summary']) ?></p>
|
|
<?php endif; ?>
|
|
```
|
|
|
|
**Keep logic minimal.** Templates should display data, not process it. Complex logic belongs in plugins.
|
|
|
|
**Use semantic HTML.** Prefer `<article>`, `<header>`, `<nav>`, `<time>` over generic `<div>` elements.
|
|
|
|
## Next Steps
|
|
|
|
- [Template Variables Reference](../03-reference/03-template-variables.md) — Complete variable list
|
|
- [Internationalization](../03-reference/04-internationalization.md) — Multi-language support
|
|
- [Configuration Reference](../03-reference/01-configuration.md) — All configuration options
|