Closed camillemonchicourt closed 3 years ago
On ne voit pas comment automatiser cela. Car dans la V2 les URL se basaient uniquement sur les noms de randos. Exemple : https://rando.ecrins-parcnational.fr/a-pied/col-de-font-froide/ Alors que dans la V3, l'URL se base sur l'ID de chaque rando. Exemple : https://rando.ecrins-parcnational.fr/trek/904177-Col-de-Font-Froide
Donc soit il faut faire toutes les redirections des pages en les listant une par une dans la conf NGINX. Une alternative pas idéale au niveau référencement mais pour que ne pas que les anciennes URL tapent sur des 404, est de renvoyer non pas sur les fiches de chaque rando, mais sur la page recherche, en passant le nom de la rando en recherche libre. Exemple : https://gtr3.ecrins-parcnational.fr/search?text=col%20de%20font%20froide
Une solution a été mise en place, au niveau applicatif plutôt qu'au niveau de NGINX, avec l'ajout d'un fichier de configuration dédié redirects.json
permettant de rediriger des pages ou des groupes de pages avec des URL exactes ou des règles.
J'ai testé en mettant ce contenu dans mon fichier de conf des redirections :
{
"rules": [
{
"source": "/a-pied/col-de-font-froide",
"destination": "/trek/904177-Col-de-Font-Froide",
"locale": false
},
{
"source": "/a-pied/:name",
"destination": "/search?rawText=:name",
"locale": false
},
{
"source": "/fr/by-walk/:name",
"destination": "/en/search?rawText=:name",
"locale": false
}
]
}
Mais ça ne me redirige pas l'URL https://gtr3.ecrins-parcnational.fr/a-pied/col-de-font-froide vers https://gtr3.ecrins-parcnational.fr//trek/904177-Col-de-Font-Froide
Peut-être à cause du locale que j'ai du indiquer et passer à false
car sinon j'avais une erreur au moment du build indiquant :
info - Using webpack 4. Reason: future.webpack5 option disabled https://nextjs.org/docs/messages/webpack5
`locale` must be undefined or false for route {"source":"/a-pied/col-de-font-froide","destination":"/trek/904177-Col-de-Font-Froide","permanent":false,"locale":true,"basePath":false}
?
OK suite aux corrections de ce matin (https://github.com/GeotrekCE/Geotrek-rando-v3/commit/dac692b6e73cebae575ee3dbf9dfb25e7ab5e6fc), ça fonctionne bien désormais. Quand on interroge https://gtr3.ecrins-parcnational.fr/a-pied/col-de-font-froide, ça nous redirige bien vers https://gtr3.ecrins-parcnational.fr/trek/904177-Col-de-Font-Froide
Voici des précisions sur la migration d'un Geotrek-rando v2 à v3, à partir de ce qui a été fait sur https://rando.ecrins-parcnational.fr.
Pour bien gérer sa migration d'un Geotrek-rando v2 à v3 de manière fluide pour les utilisateurs et optimisée pour le référencement, il est conseillé :
home.title
et home.description
)Pour suivre le référencement du portail sur Google, Bing Qwant..., vous pouvez faire une recherche spécifique dans le moteur de recherche, exemples :
site:https://rando.ecrins-parcnational.fr
(pour lister toutes les pages référencés sur ce domaine)site:https://rando.ecrins-parcnational.fr/trek
(pour lister toutes randos de la v3 indexées)site:https://rando.ecrins-parcnational.fr/service
(pour lister tous les contenus touristiques de la v3 indexés)site:https://rando.ecrins-parcnational.fr/trek/903307-Le-glacier-Blanc
(pour vérifier si une page spécifique est indexée et comment)Il est aussi fortement conseillé de booster et de suivre le référencement par Google en créant une propriété sur sa Search Console (https://search.google.com).
Une fois la propriété créée, récupérée son code de validation, renseignez le au niveau du paramètre googleSiteVerificationToken
du fichier de configuration global.json
.
Vous pouvez alors notamment indiquer un "sitemap" à Google qui est une liste de toutes les URL de votre portail et facilite et accélère grandement le référencement. Celui-ci est automatiquement généré par Geotrek-rando v3 à l'URL : https://mon-url-geotrek-rando-v3/sitemap.xml.
Vous pouvez aussi suivre l'indexation de votre site, notamment les premiers jours qui suivent la migration de v2 à v3.
Pour connaitre les pages les plus vues, vérifier que le trafic sur le site fonctionne bien, idéalement qu'il évolue avec la v3 (qui a optimisé différents points du référencement), il vous faut une propriété Google Analytics pour votre portail Geotrek-rando, et renseigner son ID dans le paramètre googleAnalyticsId
du fichier de configuration global.json
.
Si vous aviez déjà un suivi de votre trafic sur votre Geotrek-rando v2, si il s'agit d'une ancienne propriété (UA-...) vous devrez en créer une nouvelle en version 4 (G-...) avec un flux associé à votre URL.
Les statistiques de votre Geotrek-rando v2 vous seront utiles pour identifier les pages les plus visitées dans la v2 qu'il sera pertinent de rediriger explicitement vers l'URL correspondante dans la v3.
Un fichier de configuration dédié à la mise en place de règles de redirection a été intégré dans l'application : redirects.json
.
Son fonctionnement est précisé dans la documentation de customisation : https://github.com/GeotrekCE/Geotrek-rando-v3/blob/main/docs/customization.md
Il permet de rediriger des pages précises, ou de définit des règles générales.
Comme il n'est pas possible d'automatiser la redirection des pages des fiches randos de la v2 à la v3, dans notre cas, on a fait des redirections manuelles pour les randos les plus visitées. Et pour les autres, on a redirigé vers la page de recherche en passant leur nom dans la recherche textuelle.
Contenu du fichier customisation/redirects.json
pour https://rando.ecrins-parcnational.fr :
{
"rules": [
{
"source": "/a-pied/le-belvedere-de-lhomme",
"destination": "/trek/918809-Le-belvedere-de-l-Homme",
"permanent": true
},
{
"source": "/a-pied/col-de-font-froide",
"destination": "/trek/904177-Col-de-Font-Froide",
"permanent": true
},
{
"source": "/a-pied/le-lac-lauvitel",
"destination": "/trek/919156-Le-lac-Lauvitel",
"permanent": true
},
{
"source": "/a-pied/le-glacier-blanc",
"destination": "/trek/903307-Le-glacier-Blanc",
"permanent": true
},
{
"source": "/a-pied/lalpe-de-villar-darene",
"destination": "/trek/905606-L-Alpe-de-Villar-d-Arene",
"permanent": true
},
{
"source": "/a-pied/boucle-du-pigeonnier-dans-le-cirque-du-gioberney",
"destination": "/trek/903486-Boucle-du-Pigeonnier-dans-le-cirque-du-Gioberney",
"permanent": true
},
{
"source": "/a-pied/le-lac-du-lauzon",
"destination": "/trek/903484-Le-lac-du-Lauzon",
"permanent": true
},
{
"source": "/informations/a-savoir",
"destination": "/information/6-A-savoir",
"permanent": true
},
{
"source": "/informations/biodivecrins",
"destination": "/information/38-Biodiv’Écrins",
"permanent": true
},
{
"source": "/informations/le-parc-national-des-ecrins",
"destination": "/information/21-Le-Parc-national-des-Écrins",
"permanent": true
},
{
"source": "/informations/votre-avis",
"destination": "/information/26-Votre-avis-?",
"permanent": true
},
{
"source": "/fr/informations/to-read",
"destination": "/en/information/6-To-read",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/ecrins-national-park",
"destination": "/en/information/21-Ecrins-national-Park",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/national-park-houses",
"destination": "/en/information/27-National-park-houses",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/national-park-shop",
"destination": "/en/information/29-National-Park-Shop",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/buono-a-sapersi",
"destination": "/it/information/6-Buono-a-sapersi",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/parco-nazionale-degli-ecrins",
"destination": "/it/information/21-Parco-nazionale-degli-Ecrins",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/trasporti",
"destination": "/it/information/18-Trasporti",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/le-maison-du-parc",
"destination": "/it/information/27-Le-Maison-du-Parc",
"permanent": true,
"locale": false
},
{
"source": "/fr/informations/negozio-del-parco-nazionale",
"destination": "/it/information/29-Negozio-del-Parco-nazionale",
"permanent": true,
"locale": false
},
{
"source": "/a-pied/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/vtt/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/trail/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/contenu-touristique/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/fr/by-walk/:name",
"destination": "/en/search?rawText=:name",
"locale": false
},
{
"source": "/fr/a-piedi/:name",
"destination": "/it/search?rawText=:name",
"locale": false
}
]
}
Pour les redirections qui sont exactes, on indique aux moteurs de recherche qu'elles sont permanentes pour qu'il remplace l'ancienne URL par la nouvelle dans ses index.
Pour les règles générales qui renvoient des pages précises de fiches de randonnées vers la page de recherche pré-filtrée, on ne fait de redirection permanente car on ne souhaite pas qu'il remplace dans son index les pages de détail de rando par la page de recherche.
Une fois que l'indexation des nouvelles URL du portail en v3, on utilisera la Google Search Console pour lui indiquer de supprimer toutes les pages qu'il a indexé avec l'ancienne forme des URL. Dans notre cas, on lui indiquera de supprimer toutes les URL commençant pas https://rando.ecrins-parcnational.fr/a-pied
, https://rando.ecrins-parcnational.fr/vtt
, https://rando.ecrins-parcnational.fr/contenu-touristique
...
Pour les redirections des pages qui ne sont pas dans la langue définie par défaut (fr
) dans notre cas, il faut ajouter la locale par défaut dans l'URL source (c'est bizarre mais c'est comme ça que ça fonctionne, voir la doc des redirections de NextJS - https://nextjs.org/docs/api-reference/next.config.js/redirects) et indiquer "locale": false
.
Bonne migration !
Revu et corrigé dans la version 3.5.2 en mettant à jour Next.js de la version 11 à 12 et en remplaçant "Redirects" de Next.js par un système spécifique de redirection.
Les règles de redirection dans les langues secondaires ont été simplifiées, ne nécessitant plus la propriété locale
, ni le fait d'ajouter la clé de la langue par défaut dans la source.
Exemple du fichier redirects.json
du PNE pour rediriger les URL de la v2 à la v3 de Geotrek-rando :
{
"rules": [
{
"source": "/a-pied/le-belvedere-de-lhomme",
"destination": "/trek/918809-Le-belvedere-de-l-Homme",
"permanent": true
},
{
"source": "/a-pied/col-de-font-froide",
"destination": "/trek/904177-Col-de-Font-Froide",
"permanent": true
},
{
"source": "/a-pied/le-saut-du-laire/",
"destination": "/trek/904199-Le-Saut-du-Laire",
"permanent": true
},
{
"source": "/a-pied/le-lac-lauvitel",
"destination": "/trek/919156-Le-lac-Lauvitel",
"permanent": true
},
{
"source": "/a-pied/le-glacier-blanc",
"destination": "/trek/903307-Le-glacier-Blanc",
"permanent": true
},
{
"source": "/a-pied/lalpe-de-villar-darene",
"destination": "/trek/905606-L-Alpe-de-Villar-d-Arene",
"permanent": true
},
{
"source": "/a-pied/boucle-du-pigeonnier-dans-le-cirque-du-gioberney",
"destination": "/trek/903486-Boucle-du-Pigeonnier-dans-le-cirque-du-Gioberney",
"permanent": true
},
{
"source": "/a-pied/le-lac-du-lauzon",
"destination": "/trek/903484-Le-lac-du-Lauzon",
"permanent": true
},
{
"source": "/a-pied/le-circuit-des-lacs",
"destination": "/trek/903280-Le-circuit-des-lacs",
"permanent": true
},
{
"source": "/a-pied/la-cascade-de-la-pisse",
"destination": "/trek/903944-La-cascade-de-la-Pisse",
"permanent": true
},
{
"source": "/a-pied/le-lac-de-leychauda",
"destination": "/trek/903316-Le-lac-de-l-Eychauda",
"permanent": true
},
{
"source": "/a-pied/cascade-de-la-pisse-en-champsaur",
"destination": "/trek/905611-Cascade-de-la-Pisse-en-Champsaur",
"permanent": true
},
{
"source": "/a-pied/les-lacs-du-plateau-demparis",
"destination": "/trek/986655-Les-lacs-du-Plateau-d-Emparis",
"permanent": true
},
{
"source": "/a-pied/la-boucle-de-tiriere",
"destination": "/trek/903490-La-boucle-de-Tiriere",
"permanent": true
},
{
"source": "/a-pied/le-lac-de-la-muzelle",
"destination": "/trek/903460-Le-lac-de-la-Muzelle",
"permanent": true
},
{
"source": "/a-pied/la-montee-a-dormillouse",
"destination": "/trek/905618-La-montee-a-Dormillouse",
"permanent": true
},
{
"source": "/a-pied/le-refuge-et-le-lac-du-pave",
"destination": "/trek/903514-Le-refuge-et-le-lac-du-Pave",
"permanent": true
},
{
"source": "/a-pied/les-lacs-de-la-muzelle-et-du-lauvitel",
"destination": "/trek/903458-Les-lacs-de-la-Muzelle-et-du-Lauvitel",
"permanent": true
},
{
"source": "/a-pied/les-aiguilles-de-chabrieres",
"destination": "/trek/920686-Les-Aiguilles-de-Chabrieres",
"permanent": true
},
{
"source": "/a-pied/de-chabourneou-a-vallonpierre-par-le-sentier-du-ministre",
"destination": "/trek/920057-De-Chabourneou-a-Vallonpierre-par-le-sentier-du-Ministre",
"permanent": true
},
{
"source": "/informations/a-savoir",
"destination": "/information/6-A-savoir",
"permanent": true
},
{
"source": "/informations/biodivecrins",
"destination": "/information/38-Biodiv-Ecrins",
"permanent": true
},
{
"source": "/informations/le-parc-national-des-ecrins",
"destination": "/information/21-Le-Parc-national-des-Ecrins",
"permanent": true
},
{
"source": "/informations/acces-transports",
"destination": "/information/18-Acces-&-Transports",
"permanent": true
},
{
"source": "/informations/votre-avis",
"destination": "/information/26-Votre-avis-?",
"permanent": true
},
{
"source": "/informations/les-maisons-du-parc",
"destination": "/information/27-Les-Maisons-du-parc",
"permanent": true
},
{
"source": "/informations/sorties-accompagnees",
"destination": "/information/28-Sorties-accompagnees",
"permanent": true
},
{
"source": "/informations/boutique-du-parc",
"destination": "/information/29-Boutique-du-Parc",
"permanent": true
},
{
"source": "/informations/to-read",
"destination": "/en/information/6-To-read",
"permanent": true
},
{
"source": "/informations/ecrins-national-park",
"destination": "/en/information/21-Ecrins-national-Park",
"permanent": true
},
{
"source": "/informations/national-park-houses",
"destination": "/en/information/27-National-park-houses",
"permanent": true
},
{
"source": "/informations/national-park-shop",
"destination": "/en/information/29-National-Park-Shop",
"permanent": true
},
{
"source": "/informations/buono-a-sapersi",
"destination": "/it/information/6-Buono-a-sapersi",
"permanent": true
},
{
"source": "/informations/parco-nazionale-degli-ecrins",
"destination": "/it/information/21-Parco-nazionale-degli-Ecrins",
"permanent": true
},
{
"source": "/informations/trasporti",
"destination": "/it/information/18-Trasporti",
"permanent": true
},
{
"source": "/informations/le-maison-du-parc",
"destination": "/it/information/27-Le-Maison-du-Parc",
"permanent": true
},
{
"source": "/informations/negozio-del-parco-nazionale",
"destination": "/it/information/29-Negozio-del-Parco-nazionale",
"permanent": true
},
{
"source": "/a-pied/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/vtt/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/trail/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/contenu-touristique/:name",
"destination": "/search?rawText=:name"
},
{
"source": "/by-walk/:name",
"destination": "/en/search?rawText=:name"
},
{
"source": "/a-piedi/:name",
"destination": "/it/search?rawText=:name"
}
]
}
Il est possible d'automatiser la génération du fichier de redirections en faisant une requête sur la base de données de geotrek.
L'idée est de lancer une requête qui exporte les données en json et d'ensuite de modifier manullement les données pour ajouter les règles spécifiques non liés au contenus trek et touristic content.
-- Prérequis : création d'une fonction qui "slugify" les textes.
-- Sur notre jeu de données elle est sufisante mais il se peut qu'il y ait des problèmes que nous n'avons pas détecté
CREATE EXTENSION IF NOT EXISTS "unaccent";
CREATE OR REPLACE FUNCTION slugify("value" TEXT)
RETURNS TEXT AS $$
-- removes accents (diacritic signs) from a given string --
WITH "unaccented" AS (
SELECT unaccent("value") AS "value"
),
-- lowercases the string
"lowercase" AS (
SELECT lower("value") AS "value"
FROM "unaccented"
),
-- replaces anything that's not a letter, number, hyphen('-'), or underscore('_') with a hyphen('-')
"hyphenated" AS (
SELECT regexp_replace("value", '[^a-z0-9\\-_]+', '-', 'gi') AS "value"
FROM "lowercase"
),
-- trims hyphens('-') if they exist on the head or tail of the string
"trimmed" AS (
SELECT regexp_replace(regexp_replace("value", '\\-+$', ''), '^\\-', '') AS "value"
FROM "hyphenated"
)
SELECT "value" FROM "trimmed";
$$ LANGUAGE SQL STRICT IMMUTABLE;
-- Requête permettant d'exporter les redirections pour :
-- * les treks publiés en français et/ou en anglais
-- * les contenus touristiques publiés en françaiss et/ou en anglais
WITH r AS (
SELECT
slugify(tp."name_fr") || '/' || slugify(tt."name_fr") as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_fr") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_fr = TRUE
UNION
SELECT
slugify(tp."name_en") || '/' || slugify(tt."name_en") as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_en") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_en = TRUE
UNION
SELECT
'contenu-touristique/' || slugify(tt."name_fr") as source,
'service/' || tt.id || '-' || slugify(tt."name_fr") as destination
FROM public.tourism_touristiccontent AS tt
WHERE published_fr = TRUE
UNION
SELECT
'touristic-content/' || slugify(tt."name_en") as source,
'service/' || tt.id || '-' || slugify(tt."name_en") as destination
FROM public.tourism_touristiccontent AS tt
WHERE published_en = TRUE
)
SELECT
to_json(
array_agg(
jsonb_build_object(
'source' , source || '/' ,
'destination', destination,
'permanent', true
)
)
)
FROM r;
DROP FUNCTION slugify;
Wow canon !
Merci @amandine-sahl ! 👍
Testé sur une instance ça marche presque au top mais je rencontre quand même des petits soucis :
Certains de nos itinéraires ont des parenthèses dans leur intitulés et celles-ci sont transformées en tirets par la fonctions slugify()
Dans la v2 c'est bien comme cela que les URL sont générées mais pas dans la v3 où les parenthèses sont conservées dans les URL (ainsi que les majuscules mais ça ce n'est pas gênant)
Exemple avec :
URL v2 : rando-a-pied/fontbelle-vercors-diois-n-17/
URL v3: trek/37282-Fontbelle-(Vercors-Diois-n-17)
Du coup pour source
la transformation est OK mais pas pour destination
où on obtient : "37282-fontbelle-vercors-diois-n-17-" au lieu de "trek/37282-fontbelle-(vercors-diois-n-17)"
La solution que j'ai bricolé rapidement c'est de modifier la fonction slugify pour ne pas traiter les parenthèses comme caractères spéciaux afin qu'elles ne soient pas convertit en -
. Puis, de les convertir en -
seulement pour le champ source
directement dans la requête.
Fonction - ligne 15 : ajout des ( )
dans la liste des caractères valides de la regexp
"hyphenated" AS (
SELECT regexp_replace("value", '[^a-z0-9\\-_()]+', '-', 'gi') AS "value"
FROM "lowercase"
),
Requête : suppression des parenthèses de fin de chaîne et remplacement des autres par des -
WITH r AS (
SELECT
slugify(tp."name_fr") || '/' || slugify(regexp_replace(regexp_replace(tt."name_fr", '[)]+$', ''), '[()]', '-')) as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_fr") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_fr = TRUE
),
...
Il y a toujours des contrôles à faire et des petites adaptations selon son contexte mais c'est une super base ! Et même si il faut réfléchir pour adapter un peu la requête, c'est toujours plus sympa que tout saisir au kilomètre :)
Par contre je m'interroge : est-il indispensable de reformater les URL de destinations avec les intitulés des randos dans la mesure où une URL avec seulement l'ID de la rando aboutit via une redirection automatique ? Si j'accède à : https://rando.ecrins-parcnational.fr/trek/904074 je suis automatiquement redirigé vers : https://rando.ecrins-parcnational.fr/trek/904074-Vallon-de-Charges
Merci pour le partage en tout cas !!
Bien que ce ne soit pas directement lié à gtr3 je poste l'issue ici. Seach console refuse de faire le site map à cause d'un lien de page statique :
OK bien vu. C'est lié au fait que tu as des pages statiques qui ne sont pas internes mais vers des liens externes, et elles ont été ajoutées dans le sitemap (https://destination.cevennes-parcnational.fr/sitemap.xml). A voir si il faut exclure du sitemaps les pages statiques qui sont des liens externes.
A toute fin utile (pour les gros nazes de google) image expliquant comment récupérer la clé de google permettant de renseigner le paramètregoogleSiteVerificationToken
.
Merci pour ce SQL qui fonctionne très bien. Je partage ci-après mon script bash qui génère le json directement dans le dossier customization/config (variable DEST à adapter bien sûr) en utilisant _db_to_redirectsjson.sql qui est la fusion des SQL d'Amandine et xavyeah39. Le bash sauvegarde la version précédente de redirects.json. En cas d'utilisation de geotrek avec des partenaires susceptibles d'ajouter des sentiers, ça peut être utile de mettre en tâche cron.
#!/bin/bash
################################
# db_to_redirects_json.sh
################################
## Must be root user to install
if [ "$EUID" -ne 0 ]; then
echo "Please run as root user"
exit
fi
DEST=/home/geotrekadm/Geotrek-rando/customization/config/redirects.json
USR=geotrekadm
KEEPMAXFILES=5
SCRIPT_DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
# Backup current version
[ -f $DEST ] && mv $DEST $DEST-`date +%Y%m%d%H%M%S`.backup
# Execute SQL and store output to json file
sudo -u postgres psql -d geotrekdb < $SCRIPT_DIR/db_to_redirects_json.sql | grep "\[" > $DEST
# Format file to be human readable
sed -i "s/, /,\n /g;s/{/\n {\n /g;s/},/\n },/g;s/}\]/\n }\n\]/g;s/\[{/\[\n {/g" $DEST
sed -i '1s/^/{\n \"rules\":/' $DEST
# Append the json delimiter to the end
echo "}" >> $DEST
# Set file permissions
chown $USR:$USR $DEST
chmod 665 $DEST
# keeps only the KEEPMAXFILES most recent backups of redirects.json
ls -t `dirname "$DEST"`/redirects.json-*.backup | sed 1,$KEEPMAXFILES\d | while read file ; do rm $file; done
-- db_to_redirects_json.sql
-- Prérequis : création d'une fonction qui "slugify" les textes.
-- Sur notre jeu de données elle est sufisante mais il se peut qu'il y ait des problèmes que nous n'avons pas détecté
CREATE EXTENSION IF NOT EXISTS "unaccent";
CREATE OR REPLACE FUNCTION slugify("value" TEXT)
RETURNS TEXT AS $$
-- removes accents (diacritic signs) from a given string --
WITH "unaccented" AS (
SELECT unaccent("value") AS "value"
),
-- lowercases the string
"lowercase" AS (
SELECT lower("value") AS "value"
FROM "unaccented"
),
-- replaces anything that's not a letter, number, hyphen('-'), or underscore('_') with a hyphen('-')
"hyphenated" AS (
SELECT regexp_replace("value", '[^a-z0-9\\-_()]+', '-', 'gi') AS "value"
FROM "lowercase"
),
-- trims hyphens('-') if they exist on the head or tail of the string
"trimmed" AS (
SELECT regexp_replace(regexp_replace("value", '\\-+$', ''), '^\\-', '') AS "value"
FROM "hyphenated"
)
SELECT "value" FROM "trimmed";
$$ LANGUAGE SQL STRICT IMMUTABLE;
-- Requête permettant d'exporter les redirections pour :
-- * les treks publiés en français et/ou en anglais
-- * les contenus touristiques publiés en françaiss et/ou en anglais
WITH r AS (
SELECT
slugify(tp."name_fr") || '/' || slugify(tt."name_fr") as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_fr") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_fr = TRUE
UNION
SELECT
slugify(tp."name_en") || '/' || slugify(tt."name_en") as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_en") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_en = TRUE
UNION
SELECT
slugify(tp."name_es") || '/' || slugify(tt."name_es") as source,
'trek/' || tt.topo_object_id || '-' || slugify(tt."name_es") as destination
FROM trekking_trek AS tt
JOIN trekking_practice AS tp
ON tp.id = tt.practice_id
WHERE published_en = TRUE
UNION
SELECT
'contenu-touristique/' || slugify(tt."name_fr") as source,
'service/' || tt.id || '-' || slugify(tt."name_fr") as destination
FROM public.tourism_touristiccontent AS tt
WHERE published_fr = TRUE
UNION
SELECT
'touristic-content/' || slugify(tt."name_en") as source,
'service/' || tt.id || '-' || slugify(tt."name_en") as destination
FROM public.tourism_touristiccontent AS tt
WHERE published_en = TRUE
UNION
SELECT
'touristic-content/' || slugify(tt."name_es") as source,
'service/' || tt.id || '-' || slugify(tt."name_es") as destination
FROM public.tourism_touristiccontent AS tt
WHERE published_en = TRUE
)
SELECT
to_json(
array_agg(
jsonb_build_object(
'source' , '/' || source || '/' ,
'destination', '/' || destination,
'permanent', true
)
)
)
FROM r;
DROP FUNCTION slugify;
Intéressant, merci. Normalement, une fois le Geotrek-rando v2 coupé, il ne peut plus y avoir d'URL sous l'ancienne forme qui est indexée vu qu'il n'y a plus de V2, donc je vois pas trop l'usage d'un cron, mais pour la transition, c'est intéressant en effet.
Il manque les pages statiques dans vos scripts pour que ce soit mieux :
UNION
SELECT
'informations/' || slugify(ff."title_fr") as source,
'information/' || ff.id || '-' || slugify(ff."title_fr") as destination
FROM public.flatpages_flatpage AS ff
WHERE published_fr = TRUE
UNION
SELECT
'informations/' || slugify(ff."title_en") as source,
'information/' || ff.id || '-' || slugify(ff."title_en") as destination
FROM public.flatpages_flatpage AS ff
WHERE published_en = TRUE
UNION
SELECT
'informations/' || slugify(ff."title_it") as source,
'information/' || ff.id || '-' || slugify(ff."title_it") as destination
FROM public.flatpages_flatpage AS ff
WHERE published_it = TRUE
Petite information en plus :
Les urls sources sont transformées et les caractères '
sont transformés en -
.
Dans ce cas la il ne faut pas d'espace (juste collé les deux mots)
Exemple :
Le retour
{
"source": "/contenu-touristique/chambres-d-hotes",
"permanent": true,
"destination": "..."
},
Le bon format
{
"source": "/contenu-touristique/chambres-dhotes",
"permanent": true,
"destination": "..."
},
J'ai fait quelques corrections/modifications sur la fonction SQL slugify
:
"
'
’
(
)
-
en début et fin (le double \\
fait que la regexp voulait matcher \
suivi de -
)-
: pas besoin de \\
( [^\\-_]
interprété comme un range de \
jusqu'à _
dans l'ordre ASCII)-
en un unique -
CREATE EXTENSION IF NOT EXISTS "unaccent";
CREATE OR REPLACE FUNCTION slugify("value" TEXT)
RETURNS TEXT AS $$
-- removes accents (diacritic signs) from a given string --
WITH "unaccented" AS (
SELECT unaccent("value") AS "value"
),
-- lowercases the string
"lowercase" AS (
SELECT lower("value") AS "value"
FROM "unaccented"
),
-- remove special characters: " ' ’ ( )
"spec-removed" AS (
SELECT regexp_replace("value", '[\"\''’\(\)]', '', 'g') AS "value"
FROM "lowercase"
),
-- replaces anything that's not a letter, number, hyphen('-'), or underscore('_') with a hyphen('-')
"hyphenated" AS (
SELECT regexp_replace("value", '[^a-z0-9\-_]+', '-', 'gi') AS "value"
FROM "spec-removed"
),
-- collapses hyphens('-')
"collapsed" AS (
SELECT regexp_replace("value", '\-+', '-', 'g') AS "value"
FROM "hyphenated"
),
-- trims hyphens('-') if they exist on the head or tail of the string
"trimmed" AS (
SELECT regexp_replace(regexp_replace("value", '\-+$', ''), '^\-+', '') AS "value"
FROM "collapsed"
)
SELECT "value" FROM "trimmed";
$$ LANGUAGE SQL STRICT IMMUTABLE;
Fournir des règles de réécriture des pages depuis Geotrek-rando v2, notamment des pages de chaque rando et contenu touristique. En multilingue.