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:
@@ -1,13 +1,8 @@
|
||||
# Content management
|
||||
|
||||
CodePress offers two views for content management:
|
||||
CodePress uses a **tree view** (`/admin/content`) for content management: a file browser sidebar + CodeMirror editor, uniform with the plugin and theme editors. The old list/table view has been removed.
|
||||
|
||||
1. **Tree view** (`/admin/content`) — default, file browser sidebar + CodeMirror editor (uniform with plugin/theme editors)
|
||||
2. **List view** (`/admin/content-list`) — classic table with filter, upload, new folder/file per folder
|
||||
|
||||
Both work side-by-side and use the same content directory. Switch between views via the "List view"/"Tree view" buttons at the top right. The tree view offers the same functionality as the list view, but with a nested file-tree sidebar like the plugin and theme editors — for a consistent editor experience.
|
||||
|
||||
## Tree view (content)
|
||||
## Content editor
|
||||
|
||||
### File browser sidebar
|
||||
- Shows the nested file tree of `content/`
|
||||
@@ -19,6 +14,30 @@ Both work side-by-side and use the same content directory. Switch between views
|
||||
### Editable file types
|
||||
`.md` (Markdown), `.php` (PHP), `.html` (HTML) — consistent with the existing content-edit page.
|
||||
|
||||
All types support `---` frontmatter (layout, created, edited, plugins). The CMS `parseMetadata()` extracts frontmatter before processing. For PHP files, frontmatter is stripped from the output.
|
||||
|
||||
#### PHP content files
|
||||
PHP files can deliver content in two ways:
|
||||
1. **Echo** (output buffering): everything `echo`ed or outside `<?php` tags is used as content
|
||||
2. **Return** (callback style): `return '<h1>Hello</h1>';` — the returned string is used as content
|
||||
|
||||
In PHP files, the `ContentAPI` is available via `$api`:
|
||||
```php
|
||||
<?php
|
||||
$menu = $api->getMenu();
|
||||
$pages = $api->getAllPages();
|
||||
$config = $api->getConfig('site_title');
|
||||
return '<h1>' . htmlspecialchars($config) . '</h1>';
|
||||
```
|
||||
|
||||
#### Frontend URLs and file names
|
||||
The CMS searches files in order: `.md` → `.php` → `.html`. If files share the same name (e.g. `test.md` and `test.php`), `/nl/test` always opens `test.md`. To open a specific file type, add the extension to the URL:
|
||||
- `/nl/test` → opens `test.md` (or `.php` / `.html` if `.md` doesn't exist)
|
||||
- `/nl/test.php` → opens `test.php` (explicit extension)
|
||||
- `/nl/test.html` → opens `test.html` (explicit extension)
|
||||
|
||||
The preview link in the editor automatically uses the extension for non-`.md` files.
|
||||
|
||||
### Set image size (Markdown)
|
||||
The markdown editor toolbar has an "Image size" button (expand icon):
|
||||
1. Select an image in the editor in markdown format ``
|
||||
@@ -41,7 +60,7 @@ Images in `content/` are served via the `/-media/` endpoint (content/ lives outs
|
||||
- Enter a path within content (e.g. `en.page` or `blog/en.post`)
|
||||
- Choose the file type (Markdown/PHP/HTML)
|
||||
- Subfolders are created automatically
|
||||
- Frontmatter with `layout`, `author_name`, `author_email`, `created` is auto-generated
|
||||
- Frontmatter with `layout`, `created`, `edited` is auto-generated
|
||||
|
||||
### Upload a file
|
||||
- Click **Upload** to upload files to `content/`
|
||||
@@ -62,27 +81,22 @@ Images in `content/` are served via the `/-media/` endpoint (content/ lives outs
|
||||
On the editor page you can choose the layout from the layouts defined in `theme.json` (template mapping). The selected layout is stored in the frontmatter `layout:` key.
|
||||
|
||||
### Plugins on pages
|
||||
- Content plugins (from `plugin.json` with `type: "content"`) appear in the plugin selection
|
||||
- Choose which plugins run on the page
|
||||
- Only active **content plugins** (from `plugin.json` with `type: "content"`) appear in the plugin multiselect; system plugins (like Statistics/Logs/Dashboard) are excluded
|
||||
- Choose which plugins run on the page via the multiselect (use Ctrl/Cmd+click for multiple)
|
||||
- The plugin selection is stored in the frontmatter `plugins:` key
|
||||
|
||||
### Git / Backup integration (Phase 5)
|
||||
At the top of the content editor there are backup and git actions:
|
||||
- **Git init**: initializes a git repository in `content/` (if none exists yet)
|
||||
- **Commit**: commits all uncommitted changes (only if there is a git repo and there are changes)
|
||||
- **Backup**: link to the backup page (`/admin/content-backup`) for ZIP backup/restore
|
||||
- The git status badge shows the current branch, whether there are uncommitted changes, and the last commit
|
||||
### Backup integration
|
||||
At the top of the content editor there is a **Backup** button linking to the backup page (`/admin/content-backup`) for ZIP backup/restore and (if available) git versioning. Git integration in the editor sidebar has been removed; git will become a system plugin later.
|
||||
|
||||
### Frontmatter
|
||||
|
||||
The editor updates the frontmatter live when layout or plugin selection changes:
|
||||
The editor shows the **Created** and **Edited** timestamps (read-only, automatically updated on every save). The `edited:` value is refreshed on each save.
|
||||
|
||||
```markdown
|
||||
---
|
||||
layout: left_sidebar
|
||||
author_name: Admin
|
||||
author_email: admin@example.com
|
||||
created: 2026-08-19 10:30:25
|
||||
edited: 2026-08-20 14:22:01
|
||||
plugins: HTMLBlock, Navigation
|
||||
---
|
||||
|
||||
@@ -91,24 +105,6 @@ plugins: HTMLBlock, Navigation
|
||||
Content...
|
||||
```
|
||||
|
||||
## List view (content)
|
||||
|
||||
### Managing files
|
||||
|
||||
- **New folder** — Create folder structure
|
||||
- **New file** — Create a page (`.md`, `.php`, `.html`)
|
||||
- **Edit** — Modify existing content in the CodeMirror editor
|
||||
- **Rename** — Change file or folder names
|
||||
- **Move** — Move content to another folder
|
||||
- **Delete** — Remove content
|
||||
- **Rename folder** — Change a folder name
|
||||
|
||||
### Editor (content-edit)
|
||||
|
||||
- **CodeMirror** with syntax highlighting (Markdown, PHP, HTML)
|
||||
- **Toolbar** for quickly inserting Markdown
|
||||
- **Shortcuts**: Ctrl+S (save), Ctrl+N (new)
|
||||
|
||||
## Frontmatter
|
||||
|
||||
The editor updates the frontmatter live when layout or plugin selection changes:
|
||||
@@ -116,9 +112,9 @@ The editor updates the frontmatter live when layout or plugin selection changes:
|
||||
```markdown
|
||||
---
|
||||
layout: left_sidebar
|
||||
plugins:
|
||||
- HTMLBlock
|
||||
- Navigation
|
||||
created: 2026-08-19 10:30:25
|
||||
edited: 2026-08-20 14:22:01
|
||||
plugins: HTMLBlock, Navigation
|
||||
---
|
||||
|
||||
# Page title
|
||||
|
||||
@@ -1,84 +1,132 @@
|
||||
# Content API
|
||||
|
||||
The Content API is available in PHP content files (`.php`) and provides a safe, read-only interface to CMS data.
|
||||
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.
|
||||
|
||||
## Usage in PHP content files
|
||||
## 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 */
|
||||
|
||||
// Get all pages
|
||||
$allPages = $api->getAllPages();
|
||||
// Result: ['index' => 'Home', 'about-us' => 'About Us', ...]
|
||||
|
||||
// Get a specific page
|
||||
$page = $api->getPage('about-us');
|
||||
// Result: ['title' => 'About Us', 'content' => '...', 'path' => 'about-us', 'layout' => 'full_content', 'metadata' => [...]]
|
||||
|
||||
// Get menu structure
|
||||
$menu = $api->getMenu();
|
||||
// Result: [['title' => 'Home', 'path' => 'index', 'type' => 'file', 'active' => true], ...]
|
||||
|
||||
// Get config value (dot notation)
|
||||
$siteTitle = $api->getConfig('site_title');
|
||||
$searchEnabled = $api->getConfig('features.search_enabled', false);
|
||||
?>
|
||||
<h1>Hello <?= htmlspecialchars($api->getSiteTitle()) ?></h1>
|
||||
<p>Welcome to my page.</p>
|
||||
```
|
||||
|
||||
## Available methods
|
||||
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 pages as `['path' => 'title']` pairs
|
||||
- `getPage(string $path): ?array` - Specific page with `title`, `content`, `path`, `layout`, `metadata`
|
||||
- `pageExists(string $path): bool` - Check if a page exists
|
||||
- `getCurrentPageTitle(): string` - Title of current page
|
||||
- `getCurrentPagePath(): string` - Path of current page
|
||||
- `isHomepage(): bool` - Whether current page is the homepage
|
||||
- `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
|
||||
### 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 for a page
|
||||
- `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
|
||||
- `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 (e.g. `['nl', 'en']`)
|
||||
- `t(string $key): string` - Translate a language key
|
||||
- `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
|
||||
- `getSearchResults(): array` — Search results (empty if not searching)
|
||||
- `isSearching(): bool` — Whether a search is currently active
|
||||
|
||||
## Example: Show recent pages
|
||||
### Author
|
||||
|
||||
- `getPageAuthor(): array` — Author metadata from frontmatter (`author_name`, `author_email`, `created`)
|
||||
|
||||
## Examples
|
||||
|
||||
### Show recent pages
|
||||
|
||||
```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; // skip folders ?>
|
||||
<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>
|
||||
```
|
||||
|
||||
## Example: Using config values
|
||||
### Use a config value
|
||||
|
||||
```php
|
||||
---
|
||||
layout: full_content
|
||||
---
|
||||
<?php
|
||||
/** @var ContentAPI $api */
|
||||
if ($api->getConfig('features.search_enabled', false)): ?>
|
||||
@@ -89,6 +137,44 @@ if ($api->getConfig('features.search_enabled', false)): ?>
|
||||
<?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:
|
||||
@@ -108,12 +194,12 @@ $this->api->createUrl('about-us');
|
||||
$this->api->translate('home');
|
||||
```
|
||||
|
||||
Additionally, CMSAPI provides:
|
||||
In addition, CMSAPI has:
|
||||
|
||||
- `getCurrentPage(): array` - Full page data
|
||||
- `getCurrentPageUrl(): string` - URL of current page
|
||||
- `getCurrentPageFileInfo(): ?array` - File info (created, modified)
|
||||
- `getBreadcrumb(): string` - Breadcrumb HTML
|
||||
- `executePhpFile(string $filePath): string` - Execute PHP file and capture output
|
||||
- `getFileContent(string $filePath): string` - Get content from PHP/HTML/Markdown file
|
||||
- `contentFileExists(string $filename): bool` - Check if file exists in content directory
|
||||
- `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
|
||||
@@ -1,13 +1,8 @@
|
||||
# Content beheer
|
||||
|
||||
CodePress biedt twee weergaven voor content-beheer:
|
||||
CodePress gebruikt een **boom weergave** (`/admin/content`) voor content-beheer: een bestandsbrowser zijbalk + CodeMirror editor, uniform met de plugin- en thema-editors. De oude lijst/tabel weergave is verwijderd.
|
||||
|
||||
1. **Boom weergave** (`/admin/content`) — standaard, bestandsbrowser zijbalk + CodeMirror editor (uniform met plugin/theme-editors)
|
||||
2. **Lijst weergave** (`/admin/content-list`) — klassieke tabel met filter, upload, nieuwe map/bestand per map
|
||||
|
||||
Beide werken naast elkaar en gebruiken dezelfde content-map. Wissel tussen de weergaven via de "Lijst weergave"/"Boom weergave" knoppen rechtsboven. De boom weergave biedt dezelfde functionaliteit als de lijst weergave, maar dan met een geneste bestandsboom zijbalk zoals de plugin- en thema-editors — voor consistente editor-ervaring.
|
||||
|
||||
## Boom weergave (content)
|
||||
## Content editor
|
||||
|
||||
### Bestandsbrowser zijbalk
|
||||
- Toont de geneste bestandsboom van `content/`
|
||||
@@ -19,6 +14,30 @@ Beide werken naast elkaar en gebruiken dezelfde content-map. Wissel tussen de we
|
||||
### Bewerkbare bestandstypen
|
||||
`.md` (Markdown), `.php` (PHP), `.html` (HTML) — consistent met de bestaande content-edit pagina.
|
||||
|
||||
Alle types ondersteunen `---` frontmatter (layout, created, edited, plugins). De CMS `parseMetadata()` haalt de frontmatter eruit vóór verwerking. Bij PHP bestanden wordt de frontmatter uit de output verwijderd.
|
||||
|
||||
#### PHP content bestanden
|
||||
PHP bestanden kunnen op twee manieren content leveren:
|
||||
1. **Echo** (output buffering): alles wat `echo`d of outside `<?php` staat wordt als content gebruikt
|
||||
2. **Return** (callback stijl): `return '<h1>Hallo</h1>';` — de geretourneerde string wordt als content gebruikt
|
||||
|
||||
In PHP bestanden is de `ContentAPI` beschikbaar via `$api`:
|
||||
```php
|
||||
<?php
|
||||
$menu = $api->getMenu();
|
||||
$pages = $api->getAllPages();
|
||||
$config = $api->getConfig('site_title');
|
||||
return '<h1>' . htmlspecialchars($config) . '</h1>';
|
||||
```
|
||||
|
||||
#### Frontend URL's en bestandsnamen
|
||||
De CMS zoekt bestanden in volgorde: `.md` → `.php` → `.html`. Als bestanden dezelfde naam hebben (bijv. `test.md` en `test.php`), opent `/nl/test` altijd `test.md`. Om een specifiek bestandstype te openen, voeg de extensie toe aan de URL:
|
||||
- `/nl/test` → opent `test.md` (of `.php` / `.html` als `.md` niet bestaat)
|
||||
- `/nl/test.php` → opent `test.php` (expliciete extensie)
|
||||
- `/nl/test.html` → opent `test.html` (expliciete extensie)
|
||||
|
||||
De preview link in de editor gebruikt automatisch de extensie voor niet-`.md` bestanden.
|
||||
|
||||
### Image-grootte instellen (Markdown)
|
||||
In de markdown editor-toolbar staat een "Afbeelding grootte" knop (expand-icoon):
|
||||
1. Selecteer een afbeelding in de editor in markdown formaat ``
|
||||
@@ -41,7 +60,7 @@ Images in `content/` worden via de `/-media/` endpoint geserveerd (content/ staa
|
||||
- Geef een pad op binnen content (bijv. `nl.pagina` of `blog/nl.post`)
|
||||
- Kies het bestandstype (Markdown/PHP/HTML)
|
||||
- Submappen worden automatisch aangemaakt
|
||||
- Frontmatter met `layout`, `author_name`, `author_email`, `created` wordt automatisch gegenereerd
|
||||
- Frontmatter met `layout`, `created`, `edited` wordt automatisch gegenereerd
|
||||
|
||||
### Bestand uploaden
|
||||
- Klik op **Upload** om bestanden naar `content/` te uploaden
|
||||
@@ -62,27 +81,22 @@ Images in `content/` worden via de `/-media/` endpoint geserveerd (content/ staa
|
||||
Op de editor-pagina kun je de layout kiezen uit de layouts gedefinieerd in `theme.json` (template mapping). De geselecteerde layout wordt opgeslagen in de frontmatter `layout:` key.
|
||||
|
||||
### Plugins op pagina's
|
||||
- Content plugins (uit `plugin.json` met `type: "content"`) verschijnen in de plugin selectie
|
||||
- Kies welke plugins op de pagina draaien
|
||||
- Alleen actieve **content plugins** (uit `plugin.json` met `type: "content"`) verschijnen in de plugin multiselect; system plugins (zoals Statistics/Logs/Dashboard) zijn uitgesloten
|
||||
- Kies via de multiselect welke plugins op de pagina draaien (gebruik Ctrl/Cmd+klik voor meerdere)
|
||||
- De plugin selectie wordt opgeslagen in de frontmatter `plugins:` key
|
||||
|
||||
### Git / Backup integratie (Fase 5)
|
||||
Bovenaan de content-editor staan backup- en git-acties:
|
||||
- **Git init**: initialiseert een git repository in `content/` (als er nog geen is)
|
||||
- **Commit**: committed alle niet-committed wijzigingen (alleen als er een git repo is en er wijzigingen zijn)
|
||||
- **Backup**: link naar de backup-pagina (`/admin/content-backup`) voor ZIP backup/restore
|
||||
- De git status badge toont de huidige branch, of er niet-committed wijzigingen zijn, en de laatste commit
|
||||
### Backup integratie
|
||||
Bovenaan de content-editor staat een **Backup** knop die naar de backup-pagina (`/admin/content-backup`) leidt voor ZIP backup/restore en (indien beschikbaar) git versiebeheer. Git integratie in de editor-zijbalk is verwijderd; git wordt later een systeem plugin.
|
||||
|
||||
### Frontmatter
|
||||
|
||||
De editor werkt de frontmatter live bij bij wijzigingen van layout of plugin selectie:
|
||||
De editor toont de **Aangemaakt** en **Bewerkt** timestamps (read-only, automatisch bijgewerkt bij elke opslag). De `edited:` waarde wordt bij elke save vernieuwd.
|
||||
|
||||
```markdown
|
||||
---
|
||||
layout: left_sidebar
|
||||
author_name: Admin
|
||||
author_email: admin@example.com
|
||||
created: 2026-08-19 10:30:25
|
||||
edited: 2026-08-20 14:22:01
|
||||
plugins: HTMLBlock, Navigation
|
||||
---
|
||||
|
||||
@@ -91,24 +105,6 @@ plugins: HTMLBlock, Navigation
|
||||
Content...
|
||||
```
|
||||
|
||||
## Lijst weergave (content)
|
||||
|
||||
### Bestanden beheren
|
||||
|
||||
- **Nieuwe map** — Mappen structuur aanmaken
|
||||
- **Nieuw bestand** — Pagina aanmaken (`.md`, `.php`, `.html`)
|
||||
- **Bewerken** — Bestaande content wijzigen in CodeMirror editor
|
||||
- **Hernoemen** — Bestands- of mapnamen aanpassen
|
||||
- **Verplaatsen** — Content verplaatsen naar andere map
|
||||
- **Verwijderen** — Content verwijderen
|
||||
- **Map hernoemen** — Map naam wijzigen
|
||||
|
||||
### Editor (content-edit)
|
||||
|
||||
- **CodeMirror** met syntax highlighting (Markdown, PHP, HTML)
|
||||
- **Toolbar** voor snel Markdown invoeren
|
||||
- **Sneltoetsen**: Ctrl+S (opslaan), Ctrl+N (nieuw)
|
||||
|
||||
## Frontmatter
|
||||
|
||||
De editor werkt de frontmatter live bij bij wijzigingen van layout of plugin selectie:
|
||||
@@ -116,9 +112,9 @@ De editor werkt de frontmatter live bij bij wijzigingen van layout of plugin sel
|
||||
```markdown
|
||||
---
|
||||
layout: left_sidebar
|
||||
plugins:
|
||||
- HTMLBlock
|
||||
- Navigation
|
||||
created: 2026-08-19 10:30:25
|
||||
edited: 2026-08-20 14:22:01
|
||||
plugins: HTMLBlock, Navigation
|
||||
---
|
||||
|
||||
# Pagina titel
|
||||
|
||||
@@ -53,6 +53,7 @@ Via **Bewerken** (potlood-icoon) in het thema-overzicht open je de thema-editor
|
||||
|
||||
### Media invoegen in editor
|
||||
- De media-knop in de editor-toolbar opent de media-modal
|
||||
- Media wordt getoond als een collapsible **bestandsboom** (folders inklappen/uitklappen), gelijk aan de content/plugin/theme editors
|
||||
- In thema-context scant deze `themes/<naam>/assets/` (via `/admin/media-list?theme=<naam>`)
|
||||
- Snippet-formaat depends op bestandstype: markdown → ``, html/php → `<img src=...>`, andere → ruwe URL
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user