v2.6.3 (Lyra): Content multi-type handling, getAllPages() structuur, . verberg-prefix

- Content bestanden met dezelfde naam maar ander type (md/php/html) worden
  correct geserveerd: URL met extensie opent dat bestand, URL zonder extensie
  valt terug op md > php > html (resolveContentByType helper)
- Admin content editor accepteert bestanden met dezelfde naam (ander type);
  preview-knop linkt per extensie
- Frontend navigatie/directory listing/search tonen elk bestandstype apart
- getAllPages() array structuur gewijzigd naar list van
  ['path','title','type'] met type 'md'/'php'/'html'/'folder'
- Verberg-prefix logica: _ is geen verberg-prefix meer, alleen . (en -);
  admin toont wél alle . bestanden/mappen
- ContentAPI getPage()/pageExists() respecteren expliciete extensie
- Handleiding content-api.md (NL+EN) herschreven
- File-tree unificatie: _file-tree.twig + _editor-styles.twig includes
- Versie verhoogd naar 2.6.3
This commit is contained in:
2026-08-20 16:45:53 +00:00
parent 97c4d52c78
commit 6485f693dc
28 changed files with 1481 additions and 950 deletions
+136 -49
View File
@@ -1,84 +1,132 @@
# Content API
De Content API is beschikbaar in PHP content bestanden (`.php`) en biedt een veilige, read-only interface tot CMS data.
PHP content bestanden (`.php`) draaien binnen het CMS en hebben toegang tot een
veilige, read-only API via de variabele `$api`. De output van zo'n bestand wordt
automatisch in de actieve Twig layout geplaatst op de plek van `{{ content }}`.
## Gebruik in PHP content bestanden
## Hoe je content doorgeeft aan de Twig template
Er zijn **twee manieren** om content vanuit een `.php` bestand naar het Twig
template te sturen. De CMS kiest automatisch de juiste:
1. **Echo / print** — alles wat je echoot (of wat buiten `<?php ?>` tags staat)
wordt opgevangen en als `{{ content }}` in de layout geplaatst.
2. **Return** — als je `return` gebruikt met een string, wordt die string als
`{{ content }}` gebruikt (echt output wordt dan genegeerd).
Voorbeeld met echo:
```php
---
layout: full_content
---
<?php
/** @var ContentAPI $api */
// Alle pagina's ophalen
$allPages = $api->getAllPages();
// Resultaat: ['index' => 'Home', 'over-ons' => 'Over ons', ...]
// Specifieke pagina ophalen
$page = $api->getPage('over-ons');
// Resultaat: ['title' => 'Over ons', 'content' => '...', 'path' => 'over-ons', 'layout' => 'full_content', 'metadata' => [...]]
// Menu structuur ophalen
$menu = $api->getMenu();
// Resultaat: [['title' => 'Home', 'path' => 'index', 'type' => 'file', 'active' => true], ...]
// Config waarde ophalen (dot notatie)
$siteTitle = $api->getConfig('site_title');
$searchEnabled = $api->getConfig('features.search_enabled', false);
?>
<h1>Hallo <?= htmlspecialchars($api->getSiteTitle()) ?></h1>
<p>Welkom op mijn pagina.</p>
```
## Beschikbare methods
Voorbeeld met return:
```php
---
layout: full_content
---
<?php
/** @var ContentAPI $api */
return '<h1>Hallo ' . htmlspecialchars($api->getSiteTitle()) . '</h1>';
```
> De frontmatter (`---` blok) wordt door het CMS gestript vóórdat de PHP code
> uitgevoerd wordt. De layout key bepaalt welke Twig template de content omringt.
## Wat kun je níét doen
- Je kunt **geen** eigen Twig variabelen zetten vanuit een `.php` bestand.
PHP content leverde alleen de `{{ content }}` placeholder. Andere template
variabelen (`menu`, `breadcrumb`, `page_title`, etc.) worden door het CMS
vastgesteld op basis van de frontmatter en config — niet door je PHP code.
- Je kunt **geen** van buitenaf een ContentAPI aanroepen; de class wordt alleen
binnen `parsePHP()` geïnstantieerd en is nooit via een URL bereikbaar.
## Beschikbare variabelen in je PHP bestand
Binnen een `.php` content bestand zijn de volgende variabelen beschikbaar:
| Variabele | Type | Omschrijving |
|-----------|------|--------------|
| `$api` | `ContentAPI` | Read-only toegang tot CMS data |
| `$pageMetadata` | `array` | De frontmatter metadata van dit bestand |
## ContentAPI methods
### Pagina's
- `getAllPages(): array` - Alle pagina's als `['pad' => 'titel']` pairs
- `getPage(string $path): ?array` - Specifieke pagina met `title`, `content`, `path`, `layout`, `metadata`
- `pageExists(string $path): bool` - Controleer of een pagina bestaat
- `getCurrentPageTitle(): string` - Titel van huidige pagina
- `getCurrentPagePath(): string` - Pad van huidige pagina
- `isHomepage(): bool` - Of huidige pagina de homepage is
- `getAllPages(): array` Alle content entries (mappen + bestanden) als een list van `['path' => ..., 'title' => ..., 'type' => ...]`. `type` is `md`/`php`/`html` voor bestanden, `folder` voor mappen.
- `getPage(string $path): ?array` Specifieke pagina; levert `title`, `content`, `path`, `layout`, `metadata`. `$path` mag een extensie bevatten.
- `pageExists(string $path): bool` Controleer of een pagina bestaat
- `getCurrentPageTitle(): string` Titel van de huidige pagina
- `getCurrentPagePath(): string` Pad van de huidige pagina
- `isHomepage(): bool` Of de huidige pagina de homepage is
### Menu & Navigatie
### Menu & navigatie
- `getMenu(): array` - Hiërarchische menu structuur met `title`, `path`, `children`, `active`
- `buildUrl(string $page = 'index', ?string $lang = null, array $params = []): string` - Bouw een URL voor een pagina
- `getMenu(): array` Hiërarchische menu structuur met `title`, `path`, `children`, `active`
- `buildUrl(string $page = 'index', ?string $lang = null, array $params = []): string` Bouw een URL
### Configuratie
- `getConfig(string $key, mixed $default = null): mixed` - Config waarde via dot notatie (bijv. `'features.search_enabled'`)
- `getSiteTitle(): string` - Site titel uit config
- `getConfig(string $key, mixed $default = null): mixed` Config waarde via dot notatie (bijv. `'features.search_enabled'`)
- `getSiteTitle(): string` Site titel uit config
### Taal
- `getCurrentLanguage(): string` - Huidige taal code (bijv. `'nl'`)
- `getAvailableLanguages(): array` - Beschikbare talen (bijv. `['nl', 'en']`)
- `t(string $key): string` - Vertaal een language key
- `getCurrentLanguage(): string` Huidige taal code (bijv. `'nl'`)
- `getAvailableLanguages(): array` Beschikbare talen
- `t(string $key): string` Vertaal een language key
### Zoeken
- `getSearchResults(): array` - Zoekresultaten (leeg als niet aan het zoeken)
- `isSearching(): bool` - Of er momenteel gezocht wordt
- `getSearchResults(): array` Zoekresultaten (leeg als niet aan het zoeken)
- `isSearching(): bool` Of er momenteel gezocht wordt
## Voorbeeld: Recentste pagina's tonen
### Auteur
- `getPageAuthor(): array` — Auteur metadata uit frontmatter (`author_name`, `author_email`, `created`)
## Voorbeelden
### Recente pagina's tonen
```php
---
layout: full_content
---
<?php
/** @var ContentAPI $api */
$pages = $api->getAllPages();
$entries = $api->getAllPages();
$currentLang = $api->getCurrentLanguage();
?>
<ul>
<?php foreach (array_slice($pages, 0, 5, true) as $path => $title): ?>
<?php foreach ($entries as $entry): ?>
<?php if ($entry['type'] === 'folder') continue; // sla mappen over ?>
<li>
<a href="/<?= $currentLang ?>/<?= htmlspecialchars($path) ?>">
<?= htmlspecialchars($title) ?>
<a href="/<?= htmlspecialchars($currentLang) ?>/<?= htmlspecialchars($entry['path']) ?>">
<?= htmlspecialchars($entry['title']) ?>
<small>(<?= htmlspecialchars($entry['type']) ?>)</small>
</a>
</li>
<?php endforeach; ?>
</ul>
```
## Voorbeeld: Config waarde gebruiken
### Config waarde gebruiken
```php
---
layout: full_content
---
<?php
/** @var ContentAPI $api */
if ($api->getConfig('features.search_enabled', false)): ?>
@@ -89,9 +137,48 @@ if ($api->getConfig('features.search_enabled', false)): ?>
<?php endif; ?>
```
### Dynamische begroeting op basis van taal
```php
---
layout: full_content
---
<?php
/** @var ContentAPI $api */
$lang = $api->getCurrentLanguage();
$greeting = $lang === 'nl' ? 'Welkom' : 'Welcome';
?>
<h1><?= $greeting ?> op <?= htmlspecialchars($api->getSiteTitle()) ?></h1>
```
## Layout kiezen
De frontmatter `layout` key bepaalt welke Twig template je content omringt. De
beschikbare layouts staan in `themes/<actief-thema>/theme.json` onder de
`template` sectie. Bijvoorbeeld:
```yaml
---
layout: sidebar-content
---
```
Als je geen layout opgeeft, wordt `config.default_template` gebruikt, met als
fallback `full_content`. De content komt altijd terecht op de `{{ content }}`
plek in die template.
## Veiligheid
- Gebruik altijd `htmlspecialchars()` voor output van user-content of API data.
- PHP content bestanden hebben toegang tot het bestandssysteem van de server —
wees voorzichtig met `include`, `require` of `file_get_contents` op externe
paden. Blijf binnen de `content/` map.
- De ContentAPI is read-only; je kunt er geen bestanden of config mee wijzigen.
## CMSAPI (voor plugins)
Plugins gebruiken de `CMSAPI` class via `$this->api`. Deze biedt vergelijkbare methods:
Plugins gebruiken de `CMSAPI` class via `$this->api`. Deze biedt vergelijkbare
methods:
```php
$this->api->getCurrentPageTitle();
@@ -110,10 +197,10 @@ $this->api->translate('home');
Daarnaast heeft de CMSAPI:
- `getCurrentPage(): array` - Volledige pagina data
- `getCurrentPageUrl(): string` - URL van huidige pagina
- `getCurrentPageFileInfo(): ?array` - Bestandsinfo (created, modified)
- `getBreadcrumb(): string` - Breadcrumb HTML
- `executePhpFile(string $filePath): string` - Voer PHP bestand uit en vang output op
- `getFileContent(string $filePath): string` - Haal content uit PHP/HTML/Markdown bestand
- `contentFileExists(string $filename): bool` - Controleer of bestand bestaat in content map
- `getCurrentPage(): array` Volledige pagina data
- `getCurrentPageUrl(): string` URL van huidige pagina
- `getCurrentPageFileInfo(): ?array` Bestandsinfo (created, modified)
- `getBreadcrumb(): string` Breadcrumb HTML
- `executePhpFile(string $filePath): string` Voer PHP bestand uit en vang output op
- `getFileContent(string $filePath): string` Haal content uit PHP/HTML/Markdown bestand
- `contentFileExists(string $filename): bool` Controleer of bestand bestaat in content map