feat: add editable 301 redirects via dedicated panel page
All checks were successful
Deploy Production / Deploy to Production (push) Successful in 21s

- New "Redirections" page in panel (blueprint + content) with a structure
  field: old URL (text) → target page (pages field, auto-resolves URL)
- error.php controller intercepts 404s, checks redirect map, fires go() 301
- Cache (file driver) invalidated on redirections page update or slug change
- Removed catch-all route that was breaking Kirby multilingual routing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-06-01 09:47:59 +02:00
parent 91ac702f3c
commit dbeca5e095
5 changed files with 81 additions and 3 deletions

View file

@ -0,0 +1,27 @@
title: Redirections
icon: link
tabs:
content:
label: Redirections
sections:
rules:
type: fields
fields:
rules:
label: Règles de redirection (301)
type: structure
fields:
from:
label: Ancienne URL
type: text
required: true
placeholder: /BLOG/ancien-slug
help: "Chemin relatif (la casse est ignorée)"
width: 1/2
to:
label: Page cible
type: pages
multiple: false
required: true
width: 1/2

View file

@ -58,6 +58,5 @@ tabs:
accept: application/pdf
translate: false
help: Fichier téléchargé après soumission du formulaire
files: tabs/files
seo: seo/page

View file

@ -12,8 +12,23 @@ return [
'thumbs' => require __DIR__ . '/thumbs.php',
'cache' => [
'redirects' => ['type' => 'file'],
],
'hooks' => [
'page.update:after' => function ($newPage) {
if ($newPage->id() === 'redirections') {
kirby()->cache('redirects')->remove('map');
}
},
'page.changeSlug:after' => function () {
kirby()->cache('redirects')->remove('map');
},
],
'routes' => [
require(__DIR__ . '/routes/download-white-paper.php')
require(__DIR__ . '/routes/download-white-paper.php'),
],
'tobimori.seo' => [

View file

@ -31,7 +31,8 @@ return [
'a-propos' => menuItem('a-propos', 'À propos', 'users', 'pages/a-propos'),
'blog' => menuItem('blog', 'Blog', 'text', 'pages/blog'),
'white-papers' => menuItem('livres-blancs', 'Livres blancs', 'book', 'pages/livres-blancs'),
'confidentialite' => menuItem('confidentialite', 'Confidentialité', 'lock', 'pages/policy'),
'confidentialite' => menuItem('confidentialite', 'Confidentialité', 'lock', 'pages/policy'),
'redirections' => menuItem('redirections', 'Redirections', 'url', 'pages/redirections'),
'-',
'users',
'system',

View file

@ -0,0 +1,36 @@
<?php
return function ($kirby, $page, $site) {
$path = $kirby->path();
if (!str_ends_with($path, '.json')) {
$normalized = '/' . rtrim(strtolower($path), '/');
$cache = $kirby->cache('redirects');
$map = $cache->get('map');
if ($map === null) {
$map = [];
$redirectsPage = $site->find('redirections');
if ($redirectsPage) {
foreach ($redirectsPage->rules()->toStructure() as $rule) {
$from = trim($rule->from()->value());
if ($from === '') continue;
$key = '/' . trim(strtolower($from), '/');
$target = $rule->to()->toPage();
if ($target) {
$map[$key] = $target->url();
}
}
}
$cache->set('map', $map, 1440);
}
if (isset($map[$normalized])) {
go($map[$normalized], 301);
}
}
$kirby->response()->code(404);
return [];
};