- 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
205 lines
6.4 KiB
Markdown
205 lines
6.4 KiB
Markdown
# Content API
|
|
|
|
PHP content files (`.php`) run inside the CMS and have access to a safe,
|
|
read-only API via the `$api` variable. The output of such a file is
|
|
automatically placed into the active Twig layout at the `{{ content }}` spot.
|
|
|
|
## How to pass content to the Twig template
|
|
|
|
There are **two ways** to send content from a `.php` file to the Twig template.
|
|
The CMS picks the right one automatically:
|
|
|
|
1. **Echo / print** — everything you echo (or that sits outside `<?php ?>` tags)
|
|
is captured and placed as `{{ content }}` in the layout.
|
|
2. **Return** — if you `return` a string, that string is used as `{{ content }}`
|
|
(any echoed output is then ignored).
|
|
|
|
Example with echo:
|
|
|
|
```php
|
|
---
|
|
layout: full_content
|
|
---
|
|
<?php
|
|
/** @var ContentAPI $api */
|
|
?>
|
|
<h1>Hello <?= htmlspecialchars($api->getSiteTitle()) ?></h1>
|
|
<p>Welcome to my page.</p>
|
|
```
|
|
|
|
Example with return:
|
|
|
|
```php
|
|
---
|
|
layout: full_content
|
|
---
|
|
<?php
|
|
/** @var ContentAPI $api */
|
|
return '<h1>Hello ' . htmlspecialchars($api->getSiteTitle()) . '</h1>';
|
|
```
|
|
|
|
> The frontmatter (`---` block) is stripped by the CMS before the PHP code
|
|
> runs. The layout key determines which Twig template surrounds the content.
|
|
|
|
## What you cannot do
|
|
|
|
- You **cannot** set your own Twig variables from a `.php` file. PHP content
|
|
only feeds the `{{ content }}` placeholder. Other template variables
|
|
(`menu`, `breadcrumb`, `page_title`, etc.) are set by the CMS based on
|
|
frontmatter and config — not by your PHP code.
|
|
- You **cannot** call a ContentAPI from the outside; the class is only
|
|
instantiated inside `parsePHP()` and is never reachable via a URL.
|
|
|
|
## Available variables in your PHP file
|
|
|
|
The following variables are available inside a `.php` content file:
|
|
|
|
| Variable | Type | Description |
|
|
|----------|------|-------------|
|
|
| `$api` | `ContentAPI` | Read-only access to CMS data |
|
|
| `$pageMetadata` | `array` | The frontmatter metadata of this file |
|
|
|
|
## ContentAPI methods
|
|
|
|
### Pages
|
|
|
|
- `getAllPages(): array` — All content entries (folders + files) as a list of `['path' => ..., 'title' => ..., 'type' => ...]`. `type` is `md`/`php`/`html` for files, `folder` for directories.
|
|
- `getPage(string $path): ?array` — A specific page; returns `title`, `content`, `path`, `layout`, `metadata`. `$path` may include an extension.
|
|
- `pageExists(string $path): bool` — Check whether a page exists
|
|
- `getCurrentPageTitle(): string` — Title of the current page
|
|
- `getCurrentPagePath(): string` — Path of the current page
|
|
- `isHomepage(): bool` — Whether the current page is the homepage
|
|
|
|
### Menu & navigation
|
|
|
|
- `getMenu(): array` — Hierarchical menu structure with `title`, `path`, `children`, `active`
|
|
- `buildUrl(string $page = 'index', ?string $lang = null, array $params = []): string` — Build a URL
|
|
|
|
### Configuration
|
|
|
|
- `getConfig(string $key, mixed $default = null): mixed` — Config value via dot notation (e.g. `'features.search_enabled'`)
|
|
- `getSiteTitle(): string` — Site title from config
|
|
|
|
### Language
|
|
|
|
- `getCurrentLanguage(): string` — Current language code (e.g. `'nl'`)
|
|
- `getAvailableLanguages(): array` — Available languages
|
|
- `t(string $key): string` — Translate a language key
|
|
|
|
### Search
|
|
|
|
- `getSearchResults(): array` — Search results (empty if not searching)
|
|
- `isSearching(): bool` — Whether a search is currently active
|
|
|
|
### Author
|
|
|
|
- `getPageAuthor(): array` — Author metadata from frontmatter (`author_name`, `author_email`, `created`)
|
|
|
|
## Examples
|
|
|
|
### Show recent pages
|
|
|
|
```php
|
|
---
|
|
layout: full_content
|
|
---
|
|
<?php
|
|
/** @var ContentAPI $api */
|
|
$entries = $api->getAllPages();
|
|
$currentLang = $api->getCurrentLanguage();
|
|
?>
|
|
<ul>
|
|
<?php foreach ($entries as $entry): ?>
|
|
<?php if ($entry['type'] === 'folder') continue; // skip folders ?>
|
|
<li>
|
|
<a href="/<?= htmlspecialchars($currentLang) ?>/<?= htmlspecialchars($entry['path']) ?>">
|
|
<?= htmlspecialchars($entry['title']) ?>
|
|
<small>(<?= htmlspecialchars($entry['type']) ?>)</small>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
```
|
|
|
|
### Use a config value
|
|
|
|
```php
|
|
---
|
|
layout: full_content
|
|
---
|
|
<?php
|
|
/** @var ContentAPI $api */
|
|
if ($api->getConfig('features.search_enabled', false)): ?>
|
|
<form method="GET" action="">
|
|
<input type="search" name="search" placeholder="<?= $api->t('search_placeholder') ?>">
|
|
<button type="submit"><?= $api->t('search_button') ?></button>
|
|
</form>
|
|
<?php endif; ?>
|
|
```
|
|
|
|
### Dynamic greeting based on language
|
|
|
|
```php
|
|
---
|
|
layout: full_content
|
|
---
|
|
<?php
|
|
/** @var ContentAPI $api */
|
|
$lang = $api->getCurrentLanguage();
|
|
$greeting = $lang === 'nl' ? 'Welkom' : 'Welcome';
|
|
?>
|
|
<h1><?= $greeting ?> to <?= htmlspecialchars($api->getSiteTitle()) ?></h1>
|
|
```
|
|
|
|
## Choosing a layout
|
|
|
|
The frontmatter `layout` key determines which Twig template surrounds your
|
|
content. Available layouts are defined in `themes/<active-theme>/theme.json`
|
|
under the `template` section. For example:
|
|
|
|
```yaml
|
|
---
|
|
layout: sidebar-content
|
|
---
|
|
```
|
|
|
|
If you omit a layout, `config.default_template` is used, falling back to
|
|
`full_content`. The content always lands at the `{{ content }}` spot in that
|
|
template.
|
|
|
|
## Security
|
|
|
|
- Always use `htmlspecialchars()` for output of user-content or API data.
|
|
- PHP content files have access to the server filesystem — be careful with
|
|
`include`, `require` or `file_get_contents` on external paths. Stay inside
|
|
the `content/` directory.
|
|
- The ContentAPI is read-only; you cannot modify files or config through it.
|
|
|
|
## CMSAPI (for plugins)
|
|
|
|
Plugins use the `CMSAPI` class via `$this->api`. It offers similar methods:
|
|
|
|
```php
|
|
$this->api->getCurrentPageTitle();
|
|
$this->api->getCurrentPageContent();
|
|
$this->api->getMenu();
|
|
$this->api->getConfig('site_title');
|
|
$this->api->getCurrentLanguage();
|
|
$this->api->isHomepage();
|
|
$this->api->hasContent();
|
|
$this->api->getSearchResults();
|
|
$this->api->isSearching();
|
|
$this->api->getAvailableLanguages();
|
|
$this->api->createUrl('about-us');
|
|
$this->api->translate('home');
|
|
```
|
|
|
|
In addition, CMSAPI has:
|
|
|
|
- `getCurrentPage(): array` — Full page data
|
|
- `getCurrentPageUrl(): string` — URL of the current page
|
|
- `getCurrentPageFileInfo(): ?array` — File info (created, modified)
|
|
- `getBreadcrumb(): string` — Breadcrumb HTML
|
|
- `executePhpFile(string $filePath): string` — Execute a PHP file and capture output
|
|
- `getFileContent(string $filePath): string` — Get content from a PHP/HTML/Markdown file
|
|
- `contentFileExists(string $filename): bool` — Check whether a file exists in the content directory |