Raiju/route.php
2026-10-25 06:05:42 +03:00

181 lines
6.8 KiB
PHP

<?php
/**
* Front controller for Apache (production).
*
* .htaccess sends all non-static requests here. This file handles:
* - Clean URLs (strip .php extension)
* - Primary language root access (/about → /en/about)
* - Language-prefixed pages (/de/beispiel)
* - News post URLs (/news/slug, /en/news/slug)
* - JSON-based standard pages (via includes/page.php)
*
* Language detection uses SITE_LANG_DEFAULT from admin/config.php,
* so .htaccess never needs to be edited for language changes.
*/
// Keep custom front-controller integrations on the same URL contract.
require_once __DIR__ . '/includes/routing-helper.php';
raijuRoutingCanonicalize(__DIR__);
// Load config
$configPath = __DIR__ . '/admin/config.php';
if (!file_exists($configPath)) {
header('Location: /admin/setup.php');
exit;
}
require_once $configPath;
require_once __DIR__ . '/includes/page-path.php';
require_once __DIR__ . '/includes/access-guard.php';
raijuAccessEnforceMaintenance();
// Owner-only preview of an AI change set: its new pages exist only as overlay, so the router must know them.
if (isset($_GET['rj_agent_preview']) && is_file(__DIR__ . '/includes/ai/agent/preview.php')) {
require_once __DIR__ . '/includes/ai/agent/preview.php';
raijuAgentPreviewBoot();
}
$raijuPreviewHas = static fn(string $key): bool => function_exists('raijuContentOverlayFor') && raijuContentOverlayFor($key) !== null;
require_once __DIR__ . '/includes/seo-helper.php';
// Visits are counted in includes/header.php; the router schedules a fallback
// for templates whose header does not call the tracker (see analytics-helper.php).
require_once __DIR__ . '/includes/analytics-helper.php';
// Apply migration redirects before any other routing — a redirected URL
// must never accidentally match a real page slug downstream.
require_once __DIR__ . '/includes/redirect-helper.php';
applyRedirects($_SERVER['REQUEST_URI'] ?? '/');
$primaryLang = defined('SITE_LANG_DEFAULT') ? SITE_LANG_DEFAULT : 'en';
// Parse the clean URI
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$cleanUri = trim($uri, '/');
if ($cleanUri === 'sitemap.xml') {
raijuSeoServeSitemap();
}
if ($cleanUri === 'robots.txt') {
raijuSeoServeRobots();
}
// A language homepage can also be backed only by JSON.
if (preg_match('/^[a-z]{2}$/D', $cleanUri) && isset($SITE_LANGUAGES[$cleanUri])) {
$lang = $cleanUri;
$slug = 'home';
$basePath = str_ends_with((string)$uri, '/') ? '../' : '';
$langHome = __DIR__ . '/' . $lang . '/index.php';
if (is_file($langHome)) {
raijuAccessEnforceCurrentTemplatePage($lang . '_home');
raijuAnalyticsScheduleTrack($lang . '_home', $lang, 'home');
include $langHome;
exit;
}
if (is_file(raijuPageJsonPath(__DIR__, $lang, $slug)) || $raijuPreviewHas(raijuPageContentKey($lang, $slug))) {
raijuAnalyticsScheduleTrack(raijuPageContentKey($lang, $slug), $lang, $slug);
include __DIR__ . '/includes/page.php';
exit;
}
}
// Root URL → homepage
if ($cleanUri === '') {
$basePath = '';
$langHome = __DIR__ . '/' . $primaryLang . '/index.php';
$jsonHome = __DIR__ . '/content/pages/' . $primaryLang . '_home.json';
if (is_file($langHome)) {
raijuAccessEnforceCurrentTemplatePage($primaryLang . '_home');
raijuAnalyticsScheduleTrack($primaryLang . '_home', $primaryLang, 'home');
include $langHome;
} elseif (is_file($jsonHome)) {
$lang = $primaryLang;
$slug = 'home';
raijuAnalyticsScheduleTrack($primaryLang . '_home', $primaryLang, 'home');
include __DIR__ . '/includes/page.php';
} else {
echo '<p>No homepage found. Run the <a href="admin/setup.php">setup wizard</a> to get started.</p>';
}
exit;
}
// ------------------------------------------------------------------
// News post URLs: /{lang}/news/{slug} or /news/{slug}
// ------------------------------------------------------------------
if (preg_match('#^([a-z]{2})/news/([a-z0-9-]+)$#', $cleanUri, $m)) {
$currentLang = $m[1];
$_GET['slug'] = $m[2];
$basePath = raijuPageBasePath('news/' . $m[2], true);
raijuAnalyticsScheduleTrack(null, $currentLang, 'news');
include __DIR__ . '/includes/news-post.php';
exit;
}
if (preg_match('#^news/([a-z0-9-]+)$#', $cleanUri, $m)) {
$currentLang = $primaryLang;
$_GET['slug'] = $m[1];
$basePath = raijuPageBasePath('news/' . $m[1], false);
raijuAnalyticsScheduleTrack(null, $currentLang, 'news');
include __DIR__ . '/includes/news-post.php';
exit;
}
// ------------------------------------------------------------------
// Language-prefixed URL: /{lang}/{path}
// ------------------------------------------------------------------
if (preg_match('#^([a-z]{2})/(' . raijuPagePathPattern() . ')$#', $cleanUri, $m)) {
$lang = $m[1];
$slug = $m[2];
$contentPage = raijuPageContentKey($lang, $slug);
// 1. Physical PHP file
$phpFile = raijuPageTemplatePath(__DIR__, $lang, $slug);
if (is_file($phpFile)) {
$basePath = raijuPageBasePath($slug, true);
raijuAccessEnforceCurrentTemplatePage($contentPage);
raijuAnalyticsScheduleTrack($contentPage, $lang, $slug);
include $phpFile;
exit;
}
// 2. JSON content → front controller
$jsonFile = raijuPageJsonPath(__DIR__, $lang, $slug);
if (is_file($jsonFile) || $raijuPreviewHas($contentPage)) {
$basePath = raijuPageBasePath($slug, true);
raijuAnalyticsScheduleTrack($contentPage, $lang, $slug);
include __DIR__ . '/includes/page.php';
exit;
}
}
// ------------------------------------------------------------------
// Root-level path: /{path} → primary language
// ------------------------------------------------------------------
if (preg_match('#^' . raijuPagePathPattern() . '$#', $cleanUri)) {
$lang = $primaryLang;
$slug = $cleanUri;
$contentPage = raijuPageContentKey($lang, $slug);
// 1. Physical PHP file (with .php extension)
$phpFile = raijuPageTemplatePath(__DIR__, $lang, $slug);
if (is_file($phpFile)) {
$basePath = raijuPageBasePath($slug, false);
raijuAccessEnforceCurrentTemplatePage($contentPage);
raijuAnalyticsScheduleTrack($contentPage, $lang, $slug);
include $phpFile;
exit;
}
// 2. JSON content → front controller
$jsonFile = raijuPageJsonPath(__DIR__, $lang, $slug);
if (is_file($jsonFile) || $raijuPreviewHas($contentPage)) {
$basePath = raijuPageBasePath($slug, false);
raijuAnalyticsScheduleTrack($contentPage, $lang, $slug);
include __DIR__ . '/includes/page.php';
exit;
}
}
// ------------------------------------------------------------------
// Nothing matched → 404
// ------------------------------------------------------------------
http_response_code(404);
include __DIR__ . '/404.php';