- Bug: dashboard toonde 0 content (AdminPluginAPI::getContentDir() gaf relatief pad terug zonder normalisatie) - Dynamische pad-resolutie: PluginAPIInterface uitgebreid met getProjectRoot/getContentDir/getPluginsDir/getVersionInfo; CMSAPI en AdminPluginAPI implementeren deze universeel - public/index.php media-serving gebruikt $config['content_dir'] i.p.v. hardcoded /content - Navigation en Logs plugins halen paden via de API i.p.v. hardcoded dirname(__DIR__) - WordPress-stijl docblocks toegevoegd voor alle classes, methods, properties en functies (~450 docblocks, @since 2.6.5) - Security: hardcoded plaintext-wachtwoord 'admin' verwijderd uit AdminAuth.php; bij eerste installatie wordt een cryptografisch veilig wachtwoord gegenereerd (random_bytes, 16 tekens) en eenmalig op het inlogscherm getoond - Security: git-geschiedenis schoongemaakt (admin.json, admin.json.example, admin-console/config/admin.json verwijderd uit alle commits; filter-branch over alle branches + tags, gc --prune --aggressive) - README.md, README.en.md, AGENTS.md bijgewerkt - Test-scripts bijgewerkt naar clean-URL structuur + actuele ARIA-waarden - Versie verhoogd naar 2.6.5 - Tests: pentest 29/29, WCAG 25/25, functioneel 16/16, enhanced 25/25
473 lines
13 KiB
PHP
473 lines
13 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Front-end plugin-API voor content-plugins.
|
|
*
|
|
* Biedt toegang tot de CodePressCMS-instance, pagina-informatie, menu, zoekresultaten,
|
|
* vertalingen en bestandsoperaties binnen de front-end context.
|
|
*
|
|
* @since 2.6.5
|
|
*/
|
|
class CMSAPI implements PluginAPIInterface
|
|
{
|
|
/**
|
|
* De CodePressCMS-hoofdinstantie.
|
|
*
|
|
* @since 2.6.5
|
|
* @var CodePressCMS
|
|
*/
|
|
private CodePressCMS $cms;
|
|
|
|
/**
|
|
* De front-end PluginManager-instantie, optioneel via setPluginManager geïnjecteerd.
|
|
*
|
|
* @since 2.6.5
|
|
* @var PluginManager|null
|
|
*/
|
|
private ?PluginManager $pluginManager = null;
|
|
|
|
/**
|
|
* Construeer een CMSAPI-instantie met de opgegeven CodePressCMS.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param CodePressCMS $cms De CodePressCMS-hoofdinstantie.
|
|
*/
|
|
public function __construct(CodePressCMS $cms)
|
|
{
|
|
$this->cms = $cms;
|
|
}
|
|
|
|
/**
|
|
* Injecteer de front-end PluginManager zodat content-plugins hun eigen
|
|
* vertalingen via dezelfde fallback-keten kunnen resolven.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param PluginManager $pm De front-end PluginManager-instantie.
|
|
* @return void
|
|
*/
|
|
public function setPluginManager(PluginManager $pm): void
|
|
{
|
|
$this->pluginManager = $pm;
|
|
}
|
|
|
|
/**
|
|
* Haal de informatie van de huidige pagina op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<string,mixed> Pagina-informatie van de huidige pagina.
|
|
*/
|
|
public function getCurrentPage(): array
|
|
{
|
|
return $this->cms->getPage();
|
|
}
|
|
|
|
/**
|
|
* Haal de titel van de huidige pagina op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string De titel van de huidige pagina, of lege string.
|
|
*/
|
|
public function getCurrentPageTitle(): string
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['title'] ?? '';
|
|
}
|
|
|
|
/**
|
|
* Haal de inhoud van de huidige pagina op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string De HTML-inhoud van de huidige pagina, of lege string.
|
|
*/
|
|
public function getCurrentPageContent(): string
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['content'] ?? '';
|
|
}
|
|
|
|
/**
|
|
* Haal de URL van de huidige pagina op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string De URL van de huidige pagina inclusief query-parameters.
|
|
*/
|
|
public function getCurrentPageUrl(): string
|
|
{
|
|
$page = $_GET['page'] ?? $this->cms->config['default_page'];
|
|
$lang = $_GET['lang'] ?? $this->cms->config['language']['default'] ?? 'nl';
|
|
return "?page={$page}&lang={$lang}";
|
|
}
|
|
|
|
/**
|
|
* Haal de menustructuur op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<int,array<string,mixed>> De menustructuur.
|
|
*/
|
|
public function getMenu(): array
|
|
{
|
|
return $this->cms->getMenu();
|
|
}
|
|
|
|
/**
|
|
* Haal een configuratiewaarde op via dot-notatie.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $key Configuratie-sleutel in dot-notatie (bijv. 'language.default').
|
|
* @param mixed $default Standaardwaarde indien de sleutel niet bestaat.
|
|
* @return mixed De gevonden waarde of $default indien niet gevonden.
|
|
*/
|
|
public function getConfig(string $key, $default = null)
|
|
{
|
|
$keys = explode('.', $key);
|
|
$value = $this->cms->config;
|
|
|
|
foreach ($keys as $k) {
|
|
if (!isset($value[$k])) {
|
|
return $default;
|
|
}
|
|
$value = $value[$k];
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Haal de project-rootdirectory op (absoluut, genormaliseerd).
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string Absoluut pad naar de project-root.
|
|
*/
|
|
public function getProjectRoot(): string
|
|
{
|
|
return dirname(__DIR__, 3);
|
|
}
|
|
|
|
/**
|
|
* Haal het pad naar de contentdirectory op (absoluut, genormaliseerd).
|
|
*
|
|
* De CMS-config lost relatieve content_dir-waarden al op
|
|
* (zie cms/core/config.php), dus deze wordt as-is teruggegeven.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string Absoluut pad naar de contentdirectory.
|
|
*/
|
|
public function getContentDir(): string
|
|
{
|
|
return rtrim($this->cms->config['content_dir'] ?? ($this->getProjectRoot() . '/content'), '/');
|
|
}
|
|
|
|
/**
|
|
* Haal het pad naar de pluginsdirectory op (absoluut, genormaliseerd).
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string Absoluut pad naar de pluginsdirectory.
|
|
*/
|
|
public function getPluginsDir(): string
|
|
{
|
|
return $this->getProjectRoot() . '/plugins';
|
|
}
|
|
|
|
/**
|
|
* Haal de versie-informatie uit version.php op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<string,mixed> Versie-informatie met minimaal de sleutel 'version'.
|
|
*/
|
|
public function getVersionInfo(): array
|
|
{
|
|
$versionFile = $this->getProjectRoot() . '/version.php';
|
|
if (file_exists($versionFile)) {
|
|
$data = include $versionFile;
|
|
if (is_array($data)) {
|
|
return $data;
|
|
}
|
|
}
|
|
return ['version' => '0.0.0'];
|
|
}
|
|
|
|
/**
|
|
* Haal een vertaling op voor de opgegeven sleutel.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $key Vertaalsleutel.
|
|
* @return string De vertaalde string, of $key indien niet gevonden.
|
|
*/
|
|
public function translate(string $key): string
|
|
{
|
|
return $this->cms->t($key);
|
|
}
|
|
|
|
/**
|
|
* Haal de huidige content-taal op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string De huidige taalcode (bijv. 'nl', 'en').
|
|
*/
|
|
public function getCurrentLanguage(): string
|
|
{
|
|
return $this->cms->currentLanguage;
|
|
}
|
|
|
|
/**
|
|
* Controleer of de gebruiker zich op de homepage bevindt.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return bool True indien de huidige pagina de default_page is.
|
|
*/
|
|
public function isHomepage(): bool
|
|
{
|
|
$defaultPage = $this->cms->config['default_page'] ?? 'index';
|
|
$currentPage = $_GET['page'] ?? $defaultPage;
|
|
return $currentPage === $defaultPage;
|
|
}
|
|
|
|
/**
|
|
* Haal de bestandsinformatie van de huidige pagina op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<string,mixed>|null Bestandsinformatie of null indien niet aanwezig.
|
|
*/
|
|
public function getCurrentPageFileInfo(): ?array
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['file_info'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Haal de breadcrumb-HTML op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return string De gegenereerde breadcrumb-HTML.
|
|
*/
|
|
public function getBreadcrumb(): string
|
|
{
|
|
return $this->cms->generateBreadcrumb();
|
|
}
|
|
|
|
/**
|
|
* Controleer of de contentdirectory inhoud bevat.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return bool True indien de contentdirectory niet leeg is.
|
|
*/
|
|
public function hasContent(): bool
|
|
{
|
|
return !$this->cms->isContentDirEmpty();
|
|
}
|
|
|
|
/**
|
|
* Haal de zoekresultaten op indien een zoekopdracht actief is.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<int,array<string,mixed>> De zoekresultaten, of een lege array.
|
|
*/
|
|
public function getSearchResults(): array
|
|
{
|
|
if (isset($_GET['search'])) {
|
|
return $this->cms->searchResults;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Controleer of momenteel een zoekopdracht wordt uitgevoerd.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return bool True indien de search-query-parameter aanwezig is.
|
|
*/
|
|
public function isSearching(): bool
|
|
{
|
|
return isset($_GET['search']);
|
|
}
|
|
|
|
/**
|
|
* Haal de beschikbare talen op.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<int,string> Lijst met beschikbare taalcodes.
|
|
*/
|
|
public function getAvailableLanguages(): array
|
|
{
|
|
return $this->cms->getAvailableLanguages();
|
|
}
|
|
|
|
/**
|
|
* Maak een URL voor een pagina.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $page Pagina-identifier.
|
|
* @param string|null $lang Taalcode; standaard de huidige content-taal.
|
|
* @return string De gegenereerde pagina-URL met query-parameters.
|
|
*/
|
|
public function createUrl(string $page, ?string $lang = null): string
|
|
{
|
|
$lang = $lang ?? $this->getCurrentLanguage();
|
|
return "?page={$page}&lang={$lang}";
|
|
}
|
|
|
|
/**
|
|
* Voer een PHP-bestand uit en vang de output op.
|
|
*
|
|
* Het bestand moet binnen de CMS-directory liggen om willekeurige
|
|
* file-inclusion te voorkomen.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $filePath Absoluut pad naar het PHP-bestand.
|
|
* @return string De opgevangen output, of lege string indien ongeldig.
|
|
*/
|
|
public function executePhpFile(string $filePath): string
|
|
{
|
|
if (!file_exists($filePath)) {
|
|
return '';
|
|
}
|
|
|
|
// Validate file is within the CMS directory to prevent arbitrary file inclusion
|
|
$realPath = realpath($filePath);
|
|
$cmsRoot = realpath(__DIR__ . '/../../../');
|
|
if (!$realPath || !$cmsRoot || strpos($realPath, $cmsRoot) !== 0) {
|
|
return '';
|
|
}
|
|
|
|
ob_start();
|
|
include $filePath;
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Haal de inhoud op uit een PHP-, HTML- of Markdown-bestand.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $filePath Pad naar het bestand.
|
|
* @return string De bestandsinhoud (gerenderd indien PHP/Markdown), of lege string.
|
|
*/
|
|
public function getFileContent(string $filePath): string
|
|
{
|
|
if (!file_exists($filePath)) {
|
|
return '';
|
|
}
|
|
|
|
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
|
|
|
|
switch ($extension) {
|
|
case 'php':
|
|
return $this->executePhpFile($filePath);
|
|
case 'md':
|
|
$content = file_get_contents($filePath);
|
|
$result = $this->cms->parseMarkdown($content, $filePath);
|
|
return $result['content'] ?? '';
|
|
case 'html':
|
|
return file_get_contents($filePath);
|
|
default:
|
|
return file_get_contents($filePath);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Controleer of een bestand in de contentdirectory bestaat.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $filename Bestandsnaam relatief ten opzichte van de contentdirectory.
|
|
* @return bool True indien het bestand bestaat.
|
|
*/
|
|
public function contentFileExists(string $filename): bool
|
|
{
|
|
$contentDir = $this->cms->config['content_dir'];
|
|
return file_exists($contentDir . '/' . $filename);
|
|
}
|
|
|
|
/**
|
|
* Haal alle content-items (mappen + bestanden) op als lijst met entry-arrays.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<int,array<string,mixed>> Lijst met entries ['path' => ..., 'title' => ..., 'type' => ...].
|
|
*/
|
|
public function getAllPages(): array
|
|
{
|
|
return $this->cms->getAllPageTitles();
|
|
}
|
|
|
|
/**
|
|
* Haal de auteursmetadata van de huidige pagina op.
|
|
*
|
|
* Geeft author_name, author_email en created terug uit de pagina-frontmatter.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @return array<string,string> Auteursmetadata met sleutels: author_name, author_email, created.
|
|
*/
|
|
public function getPageAuthor(): array
|
|
{
|
|
$page = $this->cms->getPage();
|
|
$metadata = $page['metadata'] ?? [];
|
|
return [
|
|
'author_name' => $metadata['author_name'] ?? '',
|
|
'author_email' => $metadata['author_email'] ?? '',
|
|
'created' => $metadata['created'] ?? '',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Haal de vertalingen voor een plugin op in de front-end context.
|
|
*
|
|
* Content-plugins volgen de huidige content-taal. Fallback-keten
|
|
* (afgehandeld door PluginManager): aangevraagde taal ->
|
|
* default_language van de plugin -> lege array.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $pluginName Plugin-directorynaam.
|
|
* @param string|null $lang Override-taal; standaard de huidige content-taal.
|
|
* @return array<string,string> Vertalingen als [key => value].
|
|
*/
|
|
public function getPluginTranslations(string $pluginName, ?string $lang = null): array
|
|
{
|
|
if ($this->pluginManager === null) {
|
|
return [];
|
|
}
|
|
$lang = $lang ?? $this->cms->currentLanguage;
|
|
return $this->pluginManager->getPluginTranslations($pluginName, $lang, 'site');
|
|
}
|
|
|
|
/**
|
|
* Vertaal een enkele sleutel voor een plugin in de front-end context.
|
|
*
|
|
* @since 2.6.5
|
|
*
|
|
* @param string $key Vertaalsleutel.
|
|
* @param string $pluginName Plugin-directorynaam.
|
|
* @param string|null $lang Override-taal; standaard de huidige content-taal.
|
|
* @return string Vertaalde string, of $key indien niet gevonden.
|
|
*/
|
|
public function t(string $key, string $pluginName, ?string $lang = null): string
|
|
{
|
|
$t = $this->getPluginTranslations($pluginName, $lang);
|
|
return $t[$key] ?? $key;
|
|
}
|
|
} |