'text/css', 'js' => 'application/javascript', 'svg' => 'image/svg+xml', 'png' => 'image/png', 'jpg' => 'image/jpeg', 'ico' => 'image/x-icon', 'woff' => 'font/woff', 'woff2' => 'font/woff2', 'json' => 'application/json', ]; /** * Statische bestanden uit public/ serveren met juiste MIME-type. * * @since 2.6.4 */ // Serve static files from public/ $filePath = $publicDir . $path; if (is_file($filePath)) { $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); if (isset($mimeTypes[$ext])) { header('Content-Type: ' . $mimeTypes[$ext]); } readfile($filePath); return true; } /** * Theme-assets uit themes// serveren (bijv. /themes/default/js/theme.js). * * Path-traversal wordt afgedwongen via realpath() + prefix-controle. * * @since 2.6.4 */ // Serve theme assets from the themes/ directory (e.g. /themes/default/js/theme.js) if (preg_match('#^/themes/([^/]+)/(.+)$#', $path, $m)) { $themeName = $m[1]; $themeRel = $m[2]; $themesDir = __DIR__ . '/../themes'; $themeFile = $themesDir . '/' . $themeName . '/' . $themeRel; $realThemes = realpath($themesDir); $realFile = realpath($themeFile); if ($realFile && $realThemes && strpos($realFile, $realThemes) === 0 && is_file($realFile)) { $ext = strtolower(pathinfo($realFile, PATHINFO_EXTENSION)); if (isset($mimeTypes[$ext])) { header('Content-Type: ' . $mimeTypes[$ext]); } readfile($realFile); return true; } http_response_code(404); return true; } /** * Admin-theme-assets serveren (bijv. /admin/assets/css/bootstrap.min.js). * * Bronmap: admin/theme/default/assets/. * * @since 2.6.4 */ // Serve admin theme assets (e.g. /admin/assets/css/bootstrap.min.js) // Served from admin/theme/default/assets/ if (preg_match('#^/admin/assets/(.+)$#', $path, $m)) { $assetPath = $m[1]; $adminThemeDir = __DIR__ . '/../admin/theme/default/assets'; $assetFile = $adminThemeDir . '/' . $assetPath; $realAdminTheme = realpath($adminThemeDir); $realFile = realpath($assetFile); if ($realFile && $realAdminTheme && strpos($realFile, $realAdminTheme) === 0 && is_file($realFile)) { $ext = strtolower(pathinfo($realFile, PATHINFO_EXTENSION)); if (isset($mimeTypes[$ext])) { header('Content-Type: ' . $mimeTypes[$ext]); } readfile($realFile); return true; } http_response_code(404); return true; } /** * Plugin-assets serveren (bijv. /plugins/Navigation/assets/css/navigation.css). * * @since 2.6.4 */ // Serve plugin assets (e.g. /plugins/Navigation/assets/css/navigation.css) if (preg_match('#^/plugins/([^/]+)/assets/(.+)$#', $path, $m)) { $pluginName = $m[1]; $assetPath = $m[2]; $pluginAssetDir = __DIR__ . '/../plugins/' . $pluginName . '/assets'; $assetFile = $pluginAssetDir . '/' . $assetPath; $realPluginDir = realpath($pluginAssetDir); $realFile = realpath($assetFile); if ($realFile && $realPluginDir && strpos($realFile, $realPluginDir) === 0 && is_file($realFile)) { $ext = strtolower(pathinfo($realFile, PATHINFO_EXTENSION)); if (isset($mimeTypes[$ext])) { header('Content-Type: ' . $mimeTypes[$ext]); } readfile($realFile); return true; } http_response_code(404); return true; } /** * Admin-routes doorsturen: /admin/ → admin.php?route=. * * Default route is 'dashboard' indien geen subpad opgegeven. * * @since 2.6.4 */ // Admin routes: /admin/login → admin.php?route=login if (preg_match('#^/admin(?:/(.+))?$#', $path, $m)) { $_GET['route'] = $m[1] ?? 'dashboard'; require $publicDir . '/admin.php'; return true; } /** * Taal-geprefixte routes: /nl/ of /en/ → index.php?lang=...&page=... * * /nl/guide activeert de guide-flag met bestaande ?page=. Default valt * door naar index.php. * * @since 2.6.4 */ // Language-prefixed routes: /nl/page/path → index.php?lang=nl&page=page/path if (preg_match('#^/(nl|en)(?:/(.+))?$#', $path, $m)) { $_GET['lang'] = $m[1]; if (isset($m[2]) && $m[2] !== '') { if ($m[2] === 'guide') { // /nl/guide → set guide flag, keep existing ?page= from query string $_GET['guide'] = '1'; if (!isset($_GET['page'])) { $_GET['page'] = ''; } } else { $_GET['page'] = $m[2]; } } require $publicDir . '/index.php'; return true; } // Root or unknown → index.php require $publicDir . '/index.php'; return true;