Security: verwijder hardcoded wachtwoord, voeg random-wachtwoord-generator toe bij eerste installatie

This commit is contained in:
2026-08-27 08:57:05 +00:00
parent 6485f693dc
commit 74612aefbb
55 changed files with 6019 additions and 876 deletions
+420 -198
View File
@@ -3,36 +3,86 @@
/**
* CodePressCMS - Lightweight file-based content management system
*
* Features:
* - Markdown, PHP, and HTML content support
* - Dynamic navigation with dropdown menus
* - Search functionality
* - Breadcrumb navigation
* - Auto-linking between pages
* - Bootstrap 5 styling
* - File-based organization
* - SEO friendly URLs
* - Responsive design
*
* @author Edwin Noorlander
* @version 1.0.0
* @license MIT
* Hoofdclass van het file-based CodePress CMS.
*
* Lichtgewicht content management systeem zonder database. Ondersteunt
* Markdown, PHP en HTML content, dynamische navigatie met dropdown-menus,
* zoekfunctionaliteit, breadcrumb-navigatie, auto-linking tussen pagina's,
* Bootstrap 5 styling, file-based organisatie, SEO-vriendelijke URLs en
* responsive design.
*
* @since 2.6.4
*/
class CodePressCMS {
/**
* Configuratie-array met alle site-instellingen.
*
* @since 2.6.4
* @var array
*/
public $config;
/**
* Actieve taalcode van het huidige verzoek (bijv. 'nl' of 'en').
*
* @since 2.6.4
* @var string
*/
public $currentLanguage;
/**
* Resultaten van de laatst uitgevoerde zoekopdracht.
*
* @since 2.6.4
* @var array
*/
public $searchResults = [];
/**
* Opgebouwde menu-structuur vanuit de content-directory.
*
* @since 2.6.4
* @var array
*/
private $menu = [];
/**
* Effectieve standaardpagina die op de taal-root URL wordt geserveerd.
*
* Wordt lazy geinitialiseerd in getEffectiveDefaultPage().
*
* @since 2.6.4
* @var string|null
*/
private ?string $effectiveDefaultPage = null;
/**
* Geladen vertalingen voor de actieve taal.
*
* @since 2.6.4
* @var array
*/
private $translations = [];
/**
* PluginManager-instance die plugins en hooks beheert.
*
* @since 2.6.4
* @var PluginManager
*/
private $pluginManager;
/**
* Constructor - Initialize the CMS with configuration
*
* @param array $config Configuration array containing site settings
* Initialiseer het CMS met de opgegeven configuratie.
*
* Laadt versie-informatie, stelt de actieve taal en vertalingen in,
* initialiseert de PluginManager en de CMSAPI, bouwt het menu op en
* voert een eventuele zoekopdracht uit.
*
* @since 2.6.4
*
* @param array $config Configuration array containing site settings.
* @return void
*/
public function __construct($config) {
$this->config = $config;
@@ -62,12 +112,14 @@ class CodePressCMS {
}
/**
* Build a clean URL for a page
*
* @param string $page Page path (e.g., 'blog/leren/artikel')
* @param string|null $lang Language code
* @param array $params Additional query parameters
* @return string Clean URL
* Bouw een clean URL voor een pagina.
*
* @since 2.6.4
*
* @param string|null $page Page path (e.g., 'blog/leren/artikel').
* @param string|null $lang Language code.
* @param array $params Additional query parameters.
* @return string Clean URL.
*/
public function buildUrl($page = null, $lang = null, $params = []) {
$lang = $lang ?: $this->currentLanguage;
@@ -86,8 +138,16 @@ class CodePressCMS {
}
/**
* Sanitize page parameter to prevent XSS attacks
* Removes any characters that are not alphanumeric, dashes, underscores, or slashes
* Sanitize de pagina-parameter om XSS-aanvallen te voorkomen.
*
* Verwijdert alle tekens die niet alfanumeriek, streepje, underscore of
* slash zijn. De punt blijft behouden zodat content-bestandsextensies
* (md/php/html) in de URL overleven.
*
* @since 2.6.4
*
* @param string $page Page parameter.
* @return string Gesanitizeerde pagina-parameter of 'invalid-page'.
*/
private function sanitizePageParam(string $page): string {
// Remove any characters that could be used for XSS; keep the dot so
@@ -97,9 +157,13 @@ class CodePressCMS {
}
/**
* Resolve the effective default page (handles 'auto' mode)
* Bepaal de effectieve standaardpagina (behandelt 'auto' en 'newest' modus).
*
* @return string Page key that is served on the language root URL
* Het resultaat wordt gecached in $effectiveDefaultPage.
*
* @since 2.6.4
*
* @return string Page key that is served on the language root URL.
*/
public function getEffectiveDefaultPage(): string
{
@@ -115,11 +179,13 @@ class CodePressCMS {
}
/**
* Build a clean admin URL
*
* @param string $route Admin route
* @param array $params Additional query parameters
* @return string Clean admin URL
* Bouw een clean admin URL.
*
* @since 2.6.4
*
* @param string $route Admin route.
* @param array $params Additional query parameters.
* @return string Clean admin URL.
*/
public static function buildAdminUrl($route = '', $params = []) {
$url = $route ? '/admin/' . $route : '/admin';
@@ -130,16 +196,24 @@ class CodePressCMS {
}
/**
* Build URL string for template use (non-static, with current lang context)
* Bouw een URL-string voor gebruik in templates (niet-statisch, met huidige taal-context).
*
* @since 2.6.4
*
* @param string $page Pagina-pad.
* @param array $extraParams Extra query parameters.
* @return string Clean URL.
*/
public function url($page = 'index', $extraParams = []) {
return $this->buildUrl($page, $this->currentLanguage, $extraParams);
}
/**
* Get current language from request or config
*
* @return string Current language code
* Bepaal de actieve taal vanuit het verzoek of de configuratie.
*
* @since 2.6.4
*
* @return string Current language code.
*/
private function getCurrentLanguage() {
$lang = $_GET['lang'] ?? $this->config['language']['default'] ?? 'nl';
@@ -149,11 +223,13 @@ class CodePressCMS {
}
/**
* Get all available languages from the language directory
*
* Each language is a subdirectory under language/ containing a site.php file.
*
* @return array Available languages with their codes and names
* Verzamel alle beschikbare talen vanuit de language-directory.
*
* Elke taal is een subdirectory onder language/ met een site.php-bestand.
*
* @since 2.6.4
*
* @return array Available languages with their codes and names.
*/
public function getAvailableLanguages() {
$langDir = __DIR__ . '/../../../language/';
@@ -187,10 +263,14 @@ class CodePressCMS {
}
/**
* Load site translations for specified language
*
* @param string $lang Language code
* @return array Translations array
* Laad site-vertalingen voor de opgegeven taal.
*
* Valt terug op de standaardtaal en daarna op een lege array.
*
* @since 2.6.4
*
* @param string $lang Language code.
* @return array Translations array.
*/
private function loadTranslations($lang) {
$langDir = __DIR__ . '/../../../language/';
@@ -215,10 +295,14 @@ class CodePressCMS {
}
/**
* Get native language name for language code
*
* @param string $langCode Language code
* @return string Native language name
* Geef de native taalnaam voor een taalcode terug.
*
* Bevat een statische mapping van ISO-codes naar native taalnamen.
*
* @since 2.6.4
*
* @param string $langCode Language code.
* @return string Native language name.
*/
private function getNativeLanguageName($langCode) {
$names = [
@@ -297,18 +381,23 @@ class CodePressCMS {
}
/**
* Get translated text
*
* @param string $key Translation key
* @return string Translated text
* Geef vertaalde tekst voor een sleutel terug.
*
* @since 2.6.4
*
* @param string $key Translation key.
* @return string Translated text, of de sleutel zelf indien geen vertaling gevonden.
*/
public function t($key) {
return $this->translations[$key] ?? $key;
}
/**
* Build menu structure from content directory
*
* Bouw de menu-structuur op vanuit de content-directory.
*
* Roept de plugin-actie 'onMenuBuild' aan na het scannen.
*
* @since 2.6.4
* @return void
*/
private function buildMenu() {
@@ -317,11 +406,13 @@ class CodePressCMS {
}
/**
* Recursively scan directory for content files and folders
*
* @param string $dir Directory path to scan
* @param string $prefix Relative path prefix
* @return array Array of menu items
* Scan recursief een directory voor content-bestanden en mappen.
*
* @since 2.6.4
*
* @param string $dir Directory path to scan.
* @param string $prefix Relative path prefix.
* @return array Array of menu items.
*/
private function scanDirectory($dir, $prefix) {
if (!is_dir($dir)) return [];
@@ -375,9 +466,14 @@ class CodePressCMS {
}
/**
* Perform search across all content files
*
* @param string $query Search query string
* Voer een zoekopdracht uit over alle content-bestanden.
*
* Reset de zoekresultaten, doorzoekt de content-directory en vuurt de
* plugin-actie 'onSearch' af.
*
* @since 2.6.4
*
* @param string $query Search query string.
* @return void
*/
private function performSearch($query) {
@@ -387,11 +483,13 @@ class CodePressCMS {
}
/**
* Recursively search for query in directory files
*
* @param string $dir Directory to search in
* @param string $prefix Relative path prefix
* @param string $query Search query
* Doorzoek recursief de bestanden in een directory naar de query.
*
* @since 2.6.4
*
* @param string $dir Directory to search in.
* @param string $prefix Relative path prefix.
* @param string $query Search query.
* @return void
*/
private function searchInDirectory($dir, $prefix, $query) {
@@ -424,11 +522,13 @@ class CodePressCMS {
}
/**
* Create search snippet with highlighted query
*
* @param string $content Full content to create snippet from
* @param string $query Search query to highlight
* @return string Formatted snippet
* Maak een zoeksnippet met de gevonden query.
*
* @since 2.6.4
*
* @param string $content Full content to create snippet from.
* @param string $query Search query to highlight.
* @return string Formatted snippet.
*/
private function createSnippet($content, $query) {
$content = strip_tags($content);
@@ -441,9 +541,13 @@ class CodePressCMS {
}
/**
* Get current page content based on request
*
* @return array Page data with title and content
* Bepaal de content van de huidige pagina op basis van het verzoek.
*
* Behandelt zoek-, guide-, welcome- en 404-verzoeken, path-traversal-
* validatie, directory-listings en taal-specifieke bestandsversies.
*
* @since 2.6.4
* @return array Page data with title and content.
*/
public function getPage() {
if (isset($_GET['search'])) {
@@ -550,16 +654,19 @@ class CodePressCMS {
}
/**
* Resolve the first content file matching a base path by type priority.
* Bepaal het eerste bestaande content-bestand voor een base-path op type-prioriteit.
*
* A request without explicit extension (e.g. "test") serves the first
* existing file among test.md test.php test.html. When $preferredExt
* is given (the URL carried an extension), that type is tried first so
* /nl/test.php keeps serving test.php even when test.md also exists.
* Een verzoek zonder expliciete extensie (bijv. "test") serveert het
* eerste bestaande bestand uit test.md -> test.php -> test.html. Wanneer
* $preferredExt is opgegeven (de URL bevatte een extensie) wordt dat
* type eerst geprobeerd, zodat /nl/test.php test.php serveert ook als
* test.md bestaat.
*
* @param string $basePath Absolute path without extension
* @param string $preferredExt Optional extension to try first (md|php|html)
* @return array|null ['path' => string, 'content' => array] or null
* @since 2.6.4
*
* @param string $basePath Absolute path without extension.
* @param string|null $preferredExt Optional extension to try first (md|php|html).
* @return array|null ['path' => string, 'content' => array] or null.
*/
private function resolveContentByType(string $basePath, ?string $preferredExt = null): ?array
{
@@ -584,11 +691,13 @@ class CodePressCMS {
}
/**
* Get file information including creation and modification dates
*
* @param string $filePath Path to the file
* @param array $metadata Optional frontmatter metadata
* @return array|null File information or null if file doesn't exist
* Verzamel bestandsinformatie inclusief aanmaak- en wijzigingsdatums.
*
* @since 2.6.4
*
* @param string $filePath Path to the file.
* @param array $metadata Optional frontmatter metadata.
* @return array|null File information or null if file doesn't exist.
*/
private function getFileInfo($filePath, array $metadata = []) {
if (!file_exists($filePath)) {
@@ -624,10 +733,12 @@ class CodePressCMS {
}
/**
* Format file size in human readable format
*
* @param int $bytes File size in bytes
* @return string Formatted file size
* Format een bestandsgrootte in leesbare vorm (B, KB, MB, GB).
*
* @since 2.6.4
*
* @param int $bytes File size in bytes.
* @return string Formatted file size.
*/
private function formatFileSize($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
@@ -641,9 +752,10 @@ class CodePressCMS {
}
/**
* Get search results page content
*
* @return array Search results page data
* Bouw de content voor de zoekresultatenpagina.
*
* @since 2.6.4
* @return array Search results page data.
*/
private function getSearchResults() {
$query = $_GET['search'];
@@ -670,10 +782,15 @@ class CodePressCMS {
}
/**
* Parse metadata from content
*
* @param string $content Raw content
* @return array Parsed metadata and content without meta block
* Parse YAML-frontmatter metadata uit de content.
*
* Herkent een blok dat begint en eindigt met `---` en levert een array
* met de metadata en de content zonder het meta-blok.
*
* @since 2.6.4
*
* @param string $content Raw content.
* @return array Parsed metadata and content without meta block.
*/
private function parseMetadata($content) {
$metadata = [];
@@ -708,9 +825,13 @@ class CodePressCMS {
}
/**
* Get hosts that count as internal for external-link detection
* Geef de hosts die gelden als intern voor externe-link-detectie.
*
* @return array List of host names
* Bevat de huidige HTTP_HOST (inclusief www-variant) en eventueel de
* author-website host.
*
* @since 2.6.4
* @return array List of host names.
*/
private function getInternalHosts(): array
{
@@ -737,11 +858,14 @@ class CodePressCMS {
}
/**
* Normalize a URL: if no scheme is present, prepend https://.
* Handles hostnames stored without protocol (e.g. "noorlander.info").
* Normaliseer een URL: ontbreekt een scheme, dan wordt https:// voorgevoegd.
*
* @param string $url Raw URL or hostname
* @return string Normalized absolute URL, or '' if empty
* Behandelt hostnames die zonder protocol zijn opgeslagen (bijv. "noorlander.info").
*
* @since 2.6.4
*
* @param string $url Raw URL or hostname.
* @return string Normalized absolute URL, or '' if empty.
*/
private function normalizeUrl(string $url): string
{
@@ -756,11 +880,16 @@ class CodePressCMS {
}
/**
* Parse Markdown content to HTML using League CommonMark
*
* @param string $content Raw Markdown content
* @param string $actualFilePath Path to the source file (used for display name fallback)
* @return array Parsed content with title and body
* Parse Markdown-content naar HTML via League CommonMark.
*
* Trekt eerst frontmatter-metadata uit, bepaalt de titel en configureert
* de CommonMark-omgeving met extensies en custom afbeeldingsgrootte-syntax.
*
* @since 2.6.4
*
* @param string $content Raw Markdown content.
* @param string $actualFilePath Path to the source file (used for display name fallback).
* @return array Parsed content with title and body.
*/
public function parseMarkdown($content, $actualFilePath = '') {
// Parse metadata first
@@ -857,11 +986,16 @@ class CodePressCMS {
}
/**
* Auto-link page titles found in content
*
* @param string $content Content to process for auto-linking
* @param string $excludeTitle Title to exclude from auto-linking (current page title)
* @return string Content with auto-linked page titles
* Auto-link pagina-titels die in de content worden gevonden.
*
* Bestaande `<a>`-tags, `<h1>`-inhoud en Markdown-links worden beschermd
* tegen dubbele linking. De titel van de huidige pagina wordt overgeslagen.
*
* @since 2.6.4
*
* @param string $content Content to process for auto-linking.
* @param string $excludeTitle Title to exclude from auto-linking (current page title).
* @return string Content with auto-linked page titles.
*/
private function autoLinkPageTitles($content, $excludeTitle = '') {
$pages = $this->getAllPageTitles();
@@ -897,9 +1031,11 @@ class CodePressCMS {
}
/**
* Get all content entries (mappen + bestanden) from the content directory.
*
* @return array List of entries: ['path' => ..., 'title' => ..., 'type' => ...]
* Verzamel alle content-entries (mappen + bestanden) uit de content-directory.
*
* @since 2.6.4
*
* @return array List of entries: ['path' => ..., 'title' => ..., 'type' => ...].
* type is 'md'/'php'/'html' voor bestanden, 'folder' voor mappen.
*/
public function getAllPageTitles() {
@@ -909,17 +1045,19 @@ class CodePressCMS {
}
/**
* Recursively scan for content entries in a directory.
*
* Returns a flat list of ['path' => relativePath, 'title' => ..., 'type' => ...].
* Scan recursief een directory voor content-entries.
*
* Levert een platte lijst van ['path' => relativePath, 'title' => ..., 'type' => ...].
* Mappen krijgen type 'folder'; bestanden krijgen hun extensie als type.
* Bestanden met dezelfde naam maar ander type blijven apart (de extensie
* zit in het pad), en mappen krijgen hun eigen entry zodat de boom zichtbaar
* blijft.
*
* @param string $dir Directory to scan
* @param string $prefix Relative path prefix
* @param array &$pages Reference to pages array to populate
*
* @since 2.6.4
*
* @param string $dir Directory to scan.
* @param string $prefix Relative path prefix.
* @param array &$pages Reference to pages array to populate.
* @return void
*/
private function scanForPageTitles($dir, $prefix, &$pages) {
@@ -958,10 +1096,16 @@ class CodePressCMS {
}
/**
* Format display name from filename
*
* @param string $filename Filename without extension
* @return string Formatted display name
* Format een bestands- of mapnaam tot een leesbare weergavenaam.
*
* Verwijdert taal-prefixen (op basis van getAvailableLanguages()) en
* content-extensies, zet streepjes/underscores om in spaties en past
* special cases toe (bijv. 'ict' -> 'ICT').
*
* @since 2.6.4
*
* @param string $filename Filename without extension.
* @return string Formatted display name.
*/
private function formatDisplayName($filename) {
$filename = (string)$filename;
@@ -996,10 +1140,15 @@ class CodePressCMS {
}
/**
* Extract page title from file content
*
* @param string $filePath Path to the file
* @return string|null Extracted title or null if not found
* Extract de paginatitel uit de content van een bestand.
*
* Voor Markdown wordt het eerste H1 gebruikt, voor PHP een $title-
* variabele en voor HTML de <title>- of <h1>-tag.
*
* @since 2.6.4
*
* @param string $filePath Path to the file.
* @return string|null Extracted title or null if not found.
*/
private function extractPageTitle($filePath) {
$content = file_get_contents($filePath);
@@ -1029,10 +1178,16 @@ class CodePressCMS {
}
/**
* Parse PHP file and capture output
*
* @param string $filePath Path to PHP file
* @return array Parsed content with title and body
* Parse een PHP-bestand en vang de output op.
*
* Stelt ContentAPI en frontmatter-metadata beschikbaar aan het
* geinclude bestand. Wanneer het bestand een string returnt wordt
* die als content gebruikt in plaats van de gebufferde output.
*
* @since 2.6.4
*
* @param string $filePath Path to PHP file.
* @return array Parsed content with title and body.
*/
private function parsePHP($filePath) {
// Read file content first to extract metadata
@@ -1076,10 +1231,13 @@ class CodePressCMS {
}
/**
* Parse HTML content and extract title
*
* @param string $content Raw HTML content
* @return array Parsed content with title and body
* Parse HTML-content en extraheer de titel.
*
* @since 2.6.4
*
* @param string $content Raw HTML content.
* @param string $actualFilePath Path to the source file (used for display name fallback).
* @return array Parsed content with title and body.
*/
private function parseHTML($content, $actualFilePath = '') {
// Parse metadata first
@@ -1110,9 +1268,11 @@ class CodePressCMS {
}
/**
* Check if content directory is empty
*
* @return bool True if content directory is empty or doesn't exist
* Controleer of de content-directory leeg is.
*
* @since 2.6.4
*
* @return bool True if content directory is empty or doesn't exist.
*/
public function isContentDirEmpty() {
$contentDir = $this->config['content_dir'];
@@ -1132,12 +1292,13 @@ class CodePressCMS {
}
/**
* Get welcome page content for new installations (empty content directory)
* Geef de content voor de welkomstpagina bij nieuwe installaties.
*
* Shows a clear "new installation" message with next steps when the
* content directory has no content files yet.
* Toont een duidelijke "nieuwe installatie"-boodschap met vervolgstappen
* wanneer de content-directory nog geen content-bestanden bevat.
*
* @return array Welcome page data
* @since 2.6.4
* @return array Welcome page data.
*/
private function getWelcomePage() {
$adminUrl = $this->buildAdminUrl();
@@ -1178,9 +1339,13 @@ class CodePressCMS {
}
/**
* Get guide page content based on user language
*
* @return array Guide page data
* Geef de content van een handleiding-pagina op basis van de gebruikerstaal.
*
* Valt terug op het Engelse equivalent indien de gevraagde taal niet
* beschikbaar is. Bouwt tevens breadcrumbs op voor de handleiding.
*
* @since 2.6.4
* @return array Guide page data.
*/
private function getGuidePage() {
$lang = $this->currentLanguage;
@@ -1247,11 +1412,17 @@ class CodePressCMS {
}
/**
* Generate directory listing page
*
* @param string $pagePath Relative path to directory
* @param string $dirPath Absolute path to directory
* @return array Directory listing page data
* Genereer een directory-listingpagina.
*
* Toont de titel van de map en een lijst met alle mappen en bestanden
* daarin. Leest eventuele layout/plugins-metadata uit een index.md in
* dezelfde map.
*
* @since 2.6.4
*
* @param string $pagePath Relative path to directory.
* @param string $dirPath Absolute path to directory.
* @return array Directory listing page data.
*/
private function getDirectoryListing($pagePath, $dirPath) {
// Get the directory name from the path, not from a potential file
@@ -1351,9 +1522,10 @@ class CodePressCMS {
}
/**
* Get 404 error page content
*
* @return array 404 page data
* Geef de content van de 404-foutpagina.
*
* @since 2.6.4
* @return array 404 page data.
*/
private function getError404() {
$staticFile = __DIR__ . '/../../../admin/static/404.html';
@@ -1371,17 +1543,23 @@ class CodePressCMS {
}
/**
* Get menu structure
*
* @return array Menu structure for navigation
* Geef de opgebouwde menu-structuur terug.
*
* @since 2.6.4
* @return array Menu structure for navigation.
*/
public function getMenu() {
return $this->menu;
}
/**
* Render the complete page with template
*
* Render de volledige pagina met het actieve theme.
*
* Bepaalt pagina-data, vuurt plugin-acties af, stelt template-data samen
* (menu, sidebar, breadcrumbs, SEO, theme-assets) en echoot de
* gerenderde layout via ThemeManager.
*
* @since 2.6.4
* @return void
*/
public function render() {
@@ -1547,11 +1725,16 @@ class CodePressCMS {
}
/**
* Generate breadcrumb navigation HTML
*
* @param bool $hasSidebar Whether sidebar content exists and should show toggle
* @param array|null $guidePage Guide page data (when on a guide page)
* @return string Breadcrumb HTML
* Genereer breadcrumb-navigatie-HTML.
*
* Ondersteunt guide-pagina's, zoekpagina's en reguliere content-pagina's
* met subdirectory's. Toont optioneel een sidebar-toggle-knop.
*
* @since 2.6.4
*
* @param bool $hasSidebar Whether sidebar content exists and should show toggle.
* @param array|null $guidePage Guide page data (when on a guide page).
* @return string Breadcrumb HTML.
*/
public function generateBreadcrumb($hasSidebar = true, $guidePage = null) {
// Sidebar toggle button (shown before home icon in breadcrumb)
@@ -1630,11 +1813,16 @@ class CodePressCMS {
}
/**
* Render menu HTML with dropdown support
*
* @param array $items Menu items to render
* @param int $level Current nesting level
* @return string Rendered menu HTML
* Render menu-HTML met ondersteuning voor dropdowns.
*
* Bestanden op root-niveau worden als tabs getoond; bestanden in mappen
* als dropdown-items. Mappen zonder kinderen worden als uitgevinkt getoond.
*
* @since 2.6.4
*
* @param array $items Menu items to render.
* @param int $level Current nesting level.
* @return string Rendered menu HTML.
*/
private function renderMenu($items, $level = 0) {
$html = '';
@@ -1698,13 +1886,16 @@ class CodePressCMS {
}
/**
* Map a frontmatter layout value to a theme template key.
* Vertaal een frontmatter layout-waarde naar een theme template-sleutel.
*
* Legacy values are translated to the new theme keys. Unknown values
* are passed through so ThemeManager can fall back to default_layout.
* Legacy waarden worden omgezet naar de nieuwe theme-keys. Onbekende
* waarden worden doorgegeven zodat ThemeManager kan terugvallen op
* default_layout.
*
* @param string $layout Layout value from page metadata
* @return string Theme template key
* @since 2.6.4
*
* @param string $layout Layout value from page metadata.
* @return string Theme template key.
*/
private function mapLayoutToThemeKey(string $layout): string {
return match ($layout) {
@@ -1717,12 +1908,14 @@ class CodePressCMS {
}
/**
* Auto-detect the first available content page
* Detecteer automatisch de eerste beschikbare content-pagina.
*
* Scans the content directory for the first .md/.php/.html file
* (preferring non-language-prefixed files) and returns its page key.
* Scant de content-directory naar het eerste .md/.php/.html-bestand
* (met voorkeur voor bestanden zonder taal-prefix) en geeft de page-key terug.
*
* @return string Detected page key, or 'index' as fallback
* @since 2.6.4
*
* @return string Detected page key, or 'index' as fallback.
*/
private function detectDefaultPage(): string
{
@@ -1748,9 +1941,11 @@ class CodePressCMS {
}
/**
* Detect the newest modified page (last modified timestamp)
* Detecteer de meest recent gewijzigde of aangemaakte pagina.
*
* @return string Page key that was most recently modified or created
* @since 2.6.4
*
* @return string Page key that was most recently modified or created.
*/
private function detectNewestPage(): string
{
@@ -1825,9 +2020,10 @@ class CodePressCMS {
}
/**
* Get homepage title
*
* @return string Homepage title
* Geef de titel van de homepagina terug.
*
* @since 2.6.4
* @return string Homepage title.
*/
private function getHomepageTitle() {
// Use a generic "Home" label instead of the page name
@@ -1836,10 +2032,12 @@ class CodePressCMS {
}
/**
* Check if folder contains the currently active page
*
* @param array $children Array of child items
* @return bool True if active page is found in children
* Controleer of een map de actieve pagina bevat.
*
* @since 2.6.4
*
* @param array $children Array of child items.
* @return bool True if active page is found in children.
*/
private function folderContainsActivePage($children) {
foreach ($children as $child) {
@@ -1856,6 +2054,22 @@ class CodePressCMS {
return false;
}
/**
* Verwerk content-HTML vóór renderen.
*
* Herschrijft legacy `-/assets/`-shortcuts en `<img src>`/`<a href>`-
* attributen die wijzen naar bestanden in de content-directory naar het
* `/-media/`-endpoint, zodat deze bestanden (die buiten de webroot
* staan) correct worden geserveerd. Absolute URLs, reeds herschreven
* `/-media/`- en `/-assets/`-URLs, theme/plugin/admin-asset-URLs,
* data:-/mailto:-URLs, fragment-URLs en clean taal-voorziene
* pagina-routes blijven ongemoeid.
*
* @since 2.6.4
*
* @param string $content Content HTML to process.
* @return string Verwerkte content met herschreven media-URLs.
*/
private function processContent(string $content): string
{
// Legacy rewrite for the old -/assets/ shortcut
@@ -1872,12 +2086,20 @@ class CodePressCMS {
// - theme/plugin asset URLs (/themes/..., /plugins/..., /admin/...)
// - data: and mailto: URLs
// - fragment-only URLs (#anchor)
// - clean language-prefixed page routes (/<lang>/<page>) — these are
// valid internal page URLs (used by autoLinkPageTitles) and must not
// be rewritten to the /-media/ endpoint
$langCodes = array_keys($this->getAvailableLanguages());
$langPattern = empty($langCodes) ? '' : '|/(?:' . implode('|', array_map('preg_quote', $langCodes)) . ')/';
$skipPattern = '~^(?:[a-z][a-z0-9+.\-]*:|//|/themes/|/plugins/|/admin/|/-media/|/-assets/|data:|mailto:' . $langPattern . ')~i';
$content = preg_replace_callback(
'~(<(?:img|a)\b[^>]*\b(?:src|href)\s*=\s*")([^"]+)(")~i',
function ($m) {
function ($m) use ($skipPattern) {
$url = $m[2];
// Leave absolute, already-rewritten, asset, data: and anchor URLs alone
if (preg_match('~^(?:[a-z][a-z0-9+.\-]*:|//|/themes/|/plugins/|/admin/|/-media/|/-assets/|data:|mailto:)~i', $url)) {
// Leave absolute, already-rewritten, asset, data:, anchor and
// language-prefixed page-route URLs alone
if (preg_match($skipPattern, $url)) {
return $m[0];
}
// Strip a leading /content/ prefix if present, then prefix with /-media/