Plugin internationalisatie: plugins hebben eigen language/ mappen. Systeem plugins volgen admin taal (admin.php), content plugins volgen content taal (site.php). Fallback chain: geselecteerd -> plugin default_language -> CMS default. PluginManager/AdminPluginAPI/CMSAPI uitgebreid met getPluginTranslations()/t(). plugin.json settings ondersteunen label_key/help_key/option_label_key. Plugin uniformiteit: alle 6 plugins hebben uniforme structuur (README.md, assets/.gitkeep, language/nl|en/). plugins/README.md herschreven. guide plugin-development.md (NL+EN) volledig herschreven. Plugin editor vernieuwd: geneste bestandsbrowser zijbalk, nieuw bestand aanmaken, uploaden naar assets/, verwijderen en verplaatsen. Nieuwe routes: plugins-file-upload, plugins-file-delete, plugins-file-move. Path-traversal bescherming + protected plugins geblokkeerd. Media invoegen in editor: nieuw /admin/media-list JSON endpoint + herbruikbare _media-modal.twig include. Plugin-context scant assets/ map. editor-toolbar.js modeMap uitgebreid voor css/scss/js/json. Plugin overzicht knoppen: alleen iconen met title/aria-label. Tests: pentest 30/30, WCAG 2.1 AA 25/25.
284 lines
7.3 KiB
PHP
284 lines
7.3 KiB
PHP
<?php
|
|
|
|
class CMSAPI implements PluginAPIInterface
|
|
{
|
|
private CodePressCMS $cms;
|
|
private ?PluginManager $pluginManager = null;
|
|
|
|
public function __construct(CodePressCMS $cms)
|
|
{
|
|
$this->cms = $cms;
|
|
}
|
|
|
|
/**
|
|
* Inject the front-end PluginManager so content plugins can resolve
|
|
* their own translations through the same fallback chain.
|
|
*/
|
|
public function setPluginManager(PluginManager $pm): void
|
|
{
|
|
$this->pluginManager = $pm;
|
|
}
|
|
|
|
/**
|
|
* Get current page information
|
|
*/
|
|
public function getCurrentPage(): array
|
|
{
|
|
return $this->cms->getPage();
|
|
}
|
|
|
|
/**
|
|
* Get current page title
|
|
*/
|
|
public function getCurrentPageTitle(): string
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['title'] ?? '';
|
|
}
|
|
|
|
/**
|
|
* Get current page content
|
|
*/
|
|
public function getCurrentPageContent(): string
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['content'] ?? '';
|
|
}
|
|
|
|
/**
|
|
* Get current page URL
|
|
*/
|
|
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}";
|
|
}
|
|
|
|
/**
|
|
* Get menu structure
|
|
*/
|
|
public function getMenu(): array
|
|
{
|
|
return $this->cms->getMenu();
|
|
}
|
|
|
|
/**
|
|
* Get configuration value
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Get translation
|
|
*/
|
|
public function translate(string $key): string
|
|
{
|
|
return $this->cms->t($key);
|
|
}
|
|
|
|
/**
|
|
* Get current language
|
|
*/
|
|
public function getCurrentLanguage(): string
|
|
{
|
|
return $this->cms->currentLanguage;
|
|
}
|
|
|
|
/**
|
|
* Check if user is on homepage
|
|
*/
|
|
public function isHomepage(): bool
|
|
{
|
|
$defaultPage = $this->cms->config['default_page'] ?? 'index';
|
|
$currentPage = $_GET['page'] ?? $defaultPage;
|
|
return $currentPage === $defaultPage;
|
|
}
|
|
|
|
/**
|
|
* Get file info for current page
|
|
*/
|
|
public function getCurrentPageFileInfo(): ?array
|
|
{
|
|
$page = $this->cms->getPage();
|
|
return $page['file_info'] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get breadcrumb data
|
|
*/
|
|
public function getBreadcrumb(): string
|
|
{
|
|
return $this->cms->generateBreadcrumb();
|
|
}
|
|
|
|
/**
|
|
* Check if content directory has content
|
|
*/
|
|
public function hasContent(): bool
|
|
{
|
|
return !$this->cms->isContentDirEmpty();
|
|
}
|
|
|
|
/**
|
|
* Get search results if searching
|
|
*/
|
|
public function getSearchResults(): array
|
|
{
|
|
if (isset($_GET['search'])) {
|
|
return $this->cms->searchResults;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Check if currently searching
|
|
*/
|
|
public function isSearching(): bool
|
|
{
|
|
return isset($_GET['search']);
|
|
}
|
|
|
|
/**
|
|
* Get available languages
|
|
*/
|
|
public function getAvailableLanguages(): array
|
|
{
|
|
return $this->cms->getAvailableLanguages();
|
|
}
|
|
|
|
/**
|
|
* Create URL for page
|
|
*/
|
|
public function createUrl(string $page, ?string $lang = null): string
|
|
{
|
|
$lang = $lang ?? $this->getCurrentLanguage();
|
|
return "?page={$page}&lang={$lang}";
|
|
}
|
|
|
|
/**
|
|
* Execute PHP file and capture output
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* Get content from PHP/HTML/Markdown file
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if file exists in content directory
|
|
*/
|
|
public function contentFileExists(string $filename): bool
|
|
{
|
|
$contentDir = $this->cms->config['content_dir'];
|
|
return file_exists($contentDir . '/' . $filename);
|
|
}
|
|
|
|
/**
|
|
* Get all pages with their metadata
|
|
*/
|
|
public function getAllPages(): array
|
|
{
|
|
return $this->cms->getAllPageTitles();
|
|
}
|
|
|
|
/**
|
|
* Get the author metadata for the current page.
|
|
* Returns author_name, author_email and created from the page frontmatter.
|
|
*
|
|
* @return array Author metadata with keys: 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'] ?? '',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the translations for a plugin in the front-end context.
|
|
*
|
|
* Content plugins follow the current content language. Fallback chain
|
|
* (handled by PluginManager): requested language -> plugin
|
|
* default_language -> empty array.
|
|
*
|
|
* @param string $pluginName Plugin directory name
|
|
* @param string|null $lang Override language; defaults to the current content language
|
|
* @return array Translations [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');
|
|
}
|
|
|
|
/**
|
|
* Translate a single key for a plugin in the front-end context.
|
|
*
|
|
* @param string $key Translation key
|
|
* @param string $pluginName Plugin directory name
|
|
* @param string|null $lang Override language; defaults to the current content language
|
|
* @return string Translated string, or $key if not found
|
|
*/
|
|
public function t(string $key, string $pluginName, ?string $lang = null): string
|
|
{
|
|
$t = $this->getPluginTranslations($pluginName, $lang);
|
|
return $t[$key] ?? $key;
|
|
}
|
|
} |