diff --git a/public/.htaccess b/public/.htaccess index 77351b7..5fe8181 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -59,3 +59,6 @@ RewriteRule ^sitemap\.xml$ /sitemap.php [L] # Ajoute .php si le fichier correspondant existe RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f RewriteRule ^(.+?)/?$ /$1.php [L,QSA] + +# 404 intelligent : redirige vers l'article le plus proche +ErrorDocument 404 /index.php?action=not_found diff --git a/public/index.php b/public/index.php index ac36f28..f88a8db 100644 --- a/public/index.php +++ b/public/index.php @@ -22,10 +22,38 @@ $action = $_GET['action'] ?? 'list'; $uuid = $_GET['uuid'] ?? ''; $slug = $_GET['slug'] ?? ''; -$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site']; +$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found']; $metaRobots = in_array($action, $_noindexActions, true) ? 'noindex, nofollow' : null; unset($_noindexActions); +// ─── Recherche de l'article le plus proche et redirection 301 ──────────────── +function searchAndRedirect(string $rawPath, ArticleManager $articles): void +{ + require_once BASE_PATH . '/src/SearchEngine.php'; + $query = (string)preg_replace('/\s{2,}/', ' ', trim( + (string)preg_replace('/[^a-zA-ZÀ-ÿ0-9\s]/u', ' ', str_replace(['-', '_', '/'], ' ', $rawPath)) + )); + if ($query === '') { + return; + } + $privateCats = $articles->getPrivateCategories(); + $pool = array_values(array_filter( + $articles->getAll(true), + static function (array $a) use ($privateCats): bool { + if (strtotime((string)($a['published_at'] ?? '')) > time()) { + return false; + } + $cat = trim($a['category'] ?? ''); + return $cat === '' || !in_array($cat, $privateCats, true); + } + )); + $results = (new SearchEngine())->search($query, $pool); + if (!empty($results)) { + header('Location: /post/' . rawurlencode($results[0]['article']['slug'] ?? ''), true, 301); + exit; + } +} + // ─── Extraction de métadonnées depuis une URL ──────────────────────────────── function fetchUrlMeta(string $url): array { @@ -472,6 +500,7 @@ switch ($action) { case 'view': $article = $slug !== '' ? $articles->getBySlug($slug) : null; if (!$article) { + searchAndRedirect($slug, $articles); http_response_code(404); echo 'Article introuvable.'; exit; @@ -1826,6 +1855,29 @@ switch ($action) { include BASE_PATH . '/templates/search.php'; break; + case 'not_found': + $notFoundPath = trim( + (string)(parse_url($_SERVER['REDIRECT_URL'] ?? $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? ''), + '/' + ); + if ($notFoundPath !== '') { + searchAndRedirect($notFoundPath, $articles); + } + http_response_code(404); + ob_start(); + ?> +
Cette adresse ne correspond à aucun article.
Vous avez peut-être suivi un ancien lien.