b985c22af4ba406eb2d4453e0e8e21a24561a6e9
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) {
17 # donner un exemple d'url pour le formulaire de choix
18 define('URLS_ARBO_EXEMPLE', '/article/titre');
19 # specifier le form de config utilise pour ces urls
20 define('URLS_ARBO_CONFIG', 'arbo');
22 // TODO: une interface permettant de verifier qu'on veut effectivment modifier
23 // une adresse existante
24 defined('CONFIRMER_MODIFIER_URL') ||
define('CONFIRMER_MODIFIER_URL', false);
27 * - Comment utiliser ce jeu d'URLs ?
28 * Recopiez le fichier "htaccess.txt" du repertoire de base du site SPIP sous
29 * le sous le nom ".htaccess" (attention a ne pas ecraser d'autres reglages
30 * que vous pourriez avoir mis dans ce fichier) ; si votre site est en
31 * "sous-repertoire", vous devrez aussi editer la ligne "RewriteBase" ce fichier.
32 * Les URLs definies seront alors redirigees vers les fichiers de SPIP.
34 * Choisissez "arbo" dans les pages de configuration d'URL
36 * SPIP calculera alors ses liens sous la forme "Mon-titre-d-article".
40 * les terminaisons ne *sont pas* stockees en base, elles servent juste
41 * a rendre les url jolies ou conformes a un usage
42 * pour avoir des url terminant par html
43 * define ('_terminaison_urls_arbo', '.html');
45 * pour preciser des terminaisons particulieres pour certains types
46 * $GLOBALS['url_arbo_terminaisons']=array(
50 * 'defaut' => '.html');
52 * pour avoir des url numeriques (id) du type 12/5/4/article/23
53 * define ('_URLS_ARBO_MIN',255);
56 * pour conserver la casse des titres dans les url
57 * define ('_url_arbo_minuscules',0);
59 * pour choisir le caractere de separation titre-id en cas de doublon
60 * (ne pas utiliser '/')
61 * define ('_url_arbo_sep_id','-');
63 * pour modifier la hierarchie apparente dans la constitution des urls
64 * ex pour que les mots soient classes par groupes
65 * $GLOBALS['url_arbo_parents']=array(
66 * 'article'=>array('id_rubrique','rubrique'),
67 * 'rubrique'=>array('id_parent','rubrique'),
68 * 'breve'=>array('id_rubrique','rubrique'),
69 * 'site'=>array('id_rubrique','rubrique'),
70 * 'mot'=>array('id_groupe','groupes_mot'));
72 * pour personaliser les types
73 * $GLOBALS['url_arbo_types']=array(
74 * 'rubrique'=>'', // pas de type pour les rubriques
81 include_spip('inc/xcache');
82 if (!function_exists('Cache')) {
88 $config_urls_arbo = isset($GLOBALS['meta']['urls_arbo']) ?
unserialize($GLOBALS['meta']['urls_arbo']) : array();
89 if (!defined('_debut_urls_arbo')) {
90 define('_debut_urls_arbo', '');
92 if (!defined('_terminaison_urls_arbo')) {
93 define('_terminaison_urls_arbo', '');
95 // pour choisir le caractere de separation titre-id en cas de doublon
96 // (ne pas utiliser '/')
97 if (!defined('_url_arbo_sep_id')) {
98 define('_url_arbo_sep_id', isset($config_urls_arbo['url_arbo_sep_id']) ?
$config_urls_arbo['url_arbo_sep_id'] : '-');
100 // option pour tout passer en minuscules
101 if (!defined('_url_arbo_minuscules')) {
102 define('_url_arbo_minuscules', isset($config_urls_arbo['url_arbo_minuscules']) ?
$config_urls_arbo['url_arbo_minuscules'] : 1);
104 if (!defined('_URLS_ARBO_MAX')) {
105 define('_URLS_ARBO_MAX', isset($config_urls_arbo['URLS_ARBO_MAX']) ?
$config_urls_arbo['URLS_ARBO_MAX'] : 80);
107 if (!defined('_URLS_ARBO_MIN')) {
108 define('_URLS_ARBO_MIN', isset($config_urls_arbo['URLS_ARBO_MIN']) ?
$config_urls_arbo['URLS_ARBO_MIN'] : 3);
111 if (!defined('_url_sep_id')) {
112 define('_url_sep_id', _url_arbo_sep_id
);
115 // peut prendre plusieurs valeurs :
116 // - false pour ne pas gerer le multilinguisme => fonctionnement historique
117 // define('_url_arbo_multilang',false);
118 // - la valeur d'une langue pour forcer le calcul des URLs dans une langue donnee
119 // define('_url_arbo_multilang','en');
120 // - true pour forcer la gestion complete du multilinguisme :
121 // calcul des URLs dans toutes les langues possibles,
122 // ajout d'un premier segment fr/ dans les URLs pour definir la langue
123 // prise en compte de l'argument lang=xx dans $args au moment de generer l'URL
124 // define('_url_arbo_multilang',true);
126 if (!defined('_url_arbo_multilang')) {
127 define('_url_arbo_multilang',false);
131 // Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
132 // indiquer la table ou les chercher (articles, auteurs etc),
133 // et elles etaient retirees par les preg_match dans la fonction ci-dessous.
134 // Elles sont a present definies a "" pour avoir des URL plus jolies
135 // mais les preg_match restent necessaires pour gerer les anciens signets.
137 #define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
138 if (!defined('_MARQUEUR_URL')) {
139 define('_MARQUEUR_URL', false);
143 * Definir les parentees utilisees pour construire des urls arborescentes
145 * @param string $type
148 function url_arbo_parent($type) {
149 static $parents = null;
150 if (is_null($parents)) {
152 'article' => array('id_rubrique', 'rubrique'),
153 'rubrique' => array('id_parent', 'rubrique'),
154 'breve' => array('id_rubrique', 'rubrique'),
155 'site' => array('id_rubrique', 'rubrique')
157 if (isset($GLOBALS['url_arbo_parents']) and !isset($_REQUEST['url_arbo_parents'])) {
158 $parents = array_merge($parents, $GLOBALS['url_arbo_parents']);
162 return (isset($parents[$type]) ?
$parents[$type] : '');
166 * Definir les terminaisons des urls :
167 * / pour une rubrique
168 * .html pour une page etc..
170 * @param string $type
173 function url_arbo_terminaison($type) {
174 static $terminaison_types = null;
175 if ($terminaison_types == null) {
176 $terminaison_types = array(
179 'defaut' => defined('_terminaison_urls_arbo') ? _terminaison_urls_arbo
: '.html'
181 if (isset($GLOBALS['url_arbo_terminaisons'])) {
182 $terminaison_types = array_merge($terminaison_types, $GLOBALS['url_arbo_terminaisons']);
185 // si c'est un appel avec type='' c'est pour avoir la liste des terminaisons
187 return array_unique(array_values($terminaison_types));
189 if (isset($terminaison_types[$type])) {
190 return $terminaison_types[$type];
191 } elseif (isset($terminaison_types['defaut'])) {
192 return $terminaison_types['defaut'];
199 * Definir le prefixe qui designe le type et qu'on utilise pour chaque objet
200 * ex : "article"/truc
201 * par defaut les rubriques ne sont pas typees, mais le reste oui
203 * @param string $type
204 * @return array|string
206 function url_arbo_type($type) {
207 static $synonymes_types = null;
208 if (!$synonymes_types) {
209 $synonymes_types = array('rubrique' => '');
210 if (isset($GLOBALS['url_arbo_types']) and is_array($GLOBALS['url_arbo_types'])) {
211 $synonymes_types = array_merge($synonymes_types, $GLOBALS['url_arbo_types']);
214 // si c'est un appel avec type='' c'est pour avoir la liste inversee des synonymes
216 return array_flip($synonymes_types);
220 ($t = (isset($synonymes_types[$type]) ?
$synonymes_types[$type] : $type)) // le type ou son synonyme
221 . ($t ?
'/' : ''); // le / eventuel pour separer, si le synonyme n'est pas vide
225 * Pipeline pour creation d'une adresse : il recoit l'url propose par le
226 * precedent, un tableau indiquant le titre de l'objet, son type, son id,
227 * et doit donner en retour une chaine d'url, sans se soucier de la
228 * duplication eventuelle, qui sera geree apres
229 * https://code.spip.net/@creer_chaine_url
234 function urls_arbo_creer_chaine_url($x) {
235 // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
236 // pourrait l'utiliser pour juste ajouter la
237 $url_old = $x['data'];
238 $objet = $x['objet'];
239 include_spip('inc/filtres');
241 include_spip('action/editer_url');
242 if (!$url = url_nettoyer(
247 _url_arbo_minuscules ?
'spip_strtolower' : ''
249 $url = $objet['id_objet'];
252 // le type ou son synonyme
253 $prefixe = url_arbo_type($objet['type']);
254 if (strpos($prefixe, '<') !== false) {
255 $prefixe = extraire_multi($prefixe);
256 $prefixe = textebrut($prefixe);
258 $x['data'] = $prefixe . $url; // le titre
264 * Boucler sur le parent pour construire l'url complete a partir des segments
265 * https://code.spip.net/@declarer_url_arbo_rec
268 * @param string $type
269 * @param string $parent
270 * @param string $type_parent
271 * @param array $contexte
274 function declarer_url_arbo_rec($url, $type, $parent, $type_parent, $contexte = array()) {
275 if (is_null($parent)) {
278 // le contexte parent ne se transmet pas
279 if (isset($contexte['id_parent'])) {
280 unset($contexte['id_parent']);
282 // Si pas de parent ou si son URL est vide, on ne renvoit que l'URL de l'objet en court
283 if ($parent == 0 or !($url_parent = declarer_url_arbo($type_parent ?
$type_parent : 'rubrique', $parent, $contexte))) {
284 return rtrim($url, '/');
285 } // Sinon on renvoit l'URL de l'objet concaténée avec celle du parent
287 return rtrim($url_parent, '/') . '/' . rtrim($url, '/');
292 * Renseigner les infos les plus recentes de l'url d'un objet
293 * et de quoi la (re)construire si besoin
295 * @param string $type
296 * @param int $id_objet
297 * @param array $contexte
298 * id_parent : rubrique parent
299 * @return bool|null|array
301 function renseigner_url_arbo($type, $id_objet, $contexte = array()) {
303 $trouver_table = charger_fonction('trouver_table', 'base');
304 $desc = $trouver_table(table_objet($type));
305 $table = $desc['table'];
306 $col_id = @$desc['key']['PRIMARY KEY'];
309 } // Quand $type ne reference pas une table
310 $id_objet = intval($id_objet);
312 $id_parent = (isset($contexte['id_parent'])?
$contexte['id_parent']:null);
313 $langue = (isset($contexte['langue'])?
$contexte['langue']:'');
315 $champ_titre = $desc['titre'] ?
$desc['titre'] : 'titre';
318 $champ_parent = url_arbo_parent($type);
319 $sel_parent = ', 0 as parent';
320 $order_by_parent = '';
322 // si un parent est fourni est qu'il est legitime, on recherche une URL pour ce parent
324 and $type_parent = end($champ_parent)
325 and $url_verifier_parent_objet = charger_fonction('url_verifier_parent_objet', 'inc', true)
326 and $url_verifier_parent_objet($type, $id_objet, $type_parent, $id_parent)) {
327 $sel_parent = ', '.intval($id_parent) . ' as parent';
328 // trouver l'url qui matche le parent en premier
329 $order_by_parent = 'U.id_parent='.intval($id_parent).' DESC, ';
331 // sinon on prend son parent direct fourni par $champ_parent
332 $sel_parent = ', O.' . reset($champ_parent) . ' as parent';
333 // trouver l'url qui matche le parent en premier
334 $order_by_parent = 'O.' . reset($champ_parent) . '=U.id_parent DESC, ';
337 $order_by_langue = "U.langue='' DESC, ";
339 $order_by_langue = 'U.langue='.sql_quote($langue).' DESC, ' . $order_by_langue;
342 // Recuperer une URL propre correspondant a l'objet.
344 "U.url, U.date, U.id_parent, U.perma, U.langue, $champ_titre $sel_parent",
345 "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)",
346 "O.$col_id=$id_objet",
348 $order_by_parent . 'U.perma DESC, ' . $order_by_langue . 'U.date DESC',
352 $urls[$type][$id_objet] = $row;
353 $urls[$type][$id_objet]['type_parent'] = $champ_parent ?
end($champ_parent) : '';
356 return isset($urls[$type][$id_objet]) ?
$urls[$type][$id_objet] : null;
360 * Retrouver/Calculer l'ensemble des segments d'url d'un objet
362 * https://code.spip.net/@declarer_url_arbo
364 * @param string $type
365 * @param int $id_objet
366 * @param array $contexte
367 * id_parent : rubrique parent
368 * langue : langue courante pour laquelle on veut l'URL
371 function declarer_url_arbo($type, $id_objet, $contexte = array()) {
372 static $urls = array();
373 // utiliser un cache memoire pour aller plus vite
374 if (!is_null($C = Cache())) {
377 // contexte de langue si pas defini, en fonction de la configuration
378 if (!isset($contexte['langue'])) {
379 if (!_url_arbo_multilang
) {
380 $contexte['langue'] = '';
381 } elseif (_url_arbo_multilang
=== true) {
382 $contexte['langue'] = $GLOBALS['spip_lang'];
384 $contexte['langue'] = _url_arbo_multilang
;
388 $hash = json_encode($contexte);
390 // Se contenter de cette URL si elle existe ;
391 // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
393 // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
394 // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
395 // qui requetent en base
396 $modifier_url = (defined('_VAR_URLS') and _VAR_URLS
);
398 if (!isset($urls[$type][$id_objet][$hash]) or $modifier_url) {
399 $r = renseigner_url_arbo($type, $id_objet, $contexte);
400 // Quand $type ne reference pas une table
406 $urls[$type][$id_objet][$hash] = $r;
410 if (!isset($urls[$type][$id_objet][$hash])) {
414 $u = &$urls[$type][$id_objet][$hash];
415 $url_propre = $u['url'];
417 // si on a trouve l'url
418 // et que le parent est bon
419 // et (permanente ou pas de demande de modif)
420 if (!is_null($url_propre)
421 and $u['id_parent'] == $u['parent']
422 and ($u['perma'] or !$modifier_url)
424 return declarer_url_arbo_rec(
427 isset($u['parent']) ?
$u['parent'] : 0,
428 isset($u['type_parent']) ?
$u['type_parent'] : null,
433 // Si URL inconnue ou maj forcee sur une url non permanente, recreer une url
435 $urls_langues = array();
436 if (is_null($url_propre) or ($modifier_url and !$u['perma'])) {
438 if (_url_arbo_multilang
=== true) {
439 include_spip('inc/lang');
440 $langues = (isset($GLOBALS['meta']['langues_multilingue']) ?
$GLOBALS['meta']['langues_multilingue'] : '');
441 $langues = explode(',', $langues);
442 if ($k = array_search(_LANGUE_PAR_DEFAUT
, $langues)) {
444 array_unshift($langues, _LANGUE_PAR_DEFAUT
);
447 if (!in_array($contexte['langue'], $langues)) {
448 $langues[] = $contexte['langue'];
451 // on calcule l'URL de chaque langue utile (langue courante, langue forcee ou toutes les langues utilises)
452 $langue_courante = $GLOBALS['spip_lang'];
454 include_spip('inc/urls');
455 $objets = urls_liste_objets();
457 foreach ($langues as $l) {
461 $urls_langues[$l] = pipeline(
462 'arbo_creer_chaine_url',
464 'data' => $url_propre, // le vieux url_propre
465 'objet' => array_merge($u, array('type' => $type, 'id_objet' => $id_objet))
469 // Eviter de tamponner les URLs a l'ancienne (cas d'un article
470 // intitule "auteur2")
471 if (preg_match(',^(' . $objets . ')[0-9]*$,', $urls_langues[$l], $r)
474 $urls_langues[$l] = $urls_langues[$l] . _url_arbo_sep_id
. $id_objet;
477 // retablir la $langue_courante par securite, au cas ou on a change de langue
478 changer_langue($langue_courante);
480 $url = $urls_langues[$contexte['langue']];
484 // Pas de changement d'url ni de parent
485 if ($url == $url_propre
486 and $u['id_parent'] == $u['parent']
488 return declarer_url_arbo_rec($url_propre, $type, $u['parent'], $u['type_parent'], $contexte);
491 // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
493 include_spip('inc/autoriser');
494 $modifier_url = autoriser('modifierurl', $type, $id_objet);
496 // Verifier si l'utilisateur veut effectivement changer l'URL
498 and CONFIRMER_MODIFIER_URL
500 // on essaye pas de regenerer une url en -xxx (suffixe id anti collision)
501 and $url != preg_replace('/' . preg_quote(_url_propres_sep_id
, '/') . '.*/', '', $url_propre)
508 if ($confirmer and !_request('ok')) {
509 die("vous changez d'url ? $url_propre -> $url");
512 // on enregistre toutes les langues
513 include_spip('action/editer_url');
514 foreach ($urls_langues as $langue => $url) {
518 'id_objet' => $id_objet,
519 'id_parent' => $u['parent'],
521 'perma' => intval($u['perma'])
523 $res = url_insert($set, $confirmer, _url_arbo_sep_id
);
524 if ($langue == $contexte['langue']) {
526 $u['url'] = $set['url'];
527 $u['id_parent'] = $set['id_parent'];
529 // l'insertion a echoue,
530 //serveur out ? retourner au mieux
531 $u['url'] = $url_propre;
536 return declarer_url_arbo_rec($u['url'], $type, $u['parent'], $u['type_parent'], $contexte);
540 * Generer l'url arbo complete constituee des segments + debut + fin
542 * https://code.spip.net/@_generer_url_arbo
544 * @param string $type
546 * @param string $args
547 * @param string $ancre
550 function _generer_url_arbo($type, $id, $args = '', $ancre = '') {
551 if ($generer_url_externe = charger_fonction("generer_url_$type", 'urls', true)) {
552 $url = $generer_url_externe($id, $args, $ancre);
563 parse_str($args, $contexte);
564 // choisir le contexte de langue en fonction de la configuration
566 if (_url_arbo_multilang
=== true) {
567 if (isset($contexte['lang']) and $contexte['lang']) {
568 $c['langue'] = $contexte['lang'];
569 $debut_langue = $c['langue'] .'/';
570 unset($contexte['lang']);
571 $args = http_build_query($contexte);
572 } elseif (isset($GLOBALS['spip_lang']) and $GLOBALS['spip_lang']) {
573 $c['langue'] = $GLOBALS['spip_lang'];
574 $debut_langue = $c['langue'] .'/';
576 } elseif (_url_arbo_multilang
) {
577 $c['langue'] = _url_arbo_multilang
;
579 $propre = declarer_url_arbo($type, $id, $c);
581 // si le parent est fourni en contexte dans le $args, verifier si l'URL relative a ce parent est la meme ou non
582 $champ_parent = url_arbo_parent($type);
584 and $champ_parent = reset($champ_parent)
585 and isset($contexte[$champ_parent]) and $contexte[$champ_parent]) {
586 $c['id_parent'] = $contexte[$champ_parent];
587 $propre_contexte = declarer_url_arbo($type, $id, $c);
588 // si l'URL est differente on la prend et on enleve l'argument de l'URL (redondance puisque parent defini par l'URL elle meme)
589 if ($propre_contexte !== $propre) {
590 $propre = $propre_contexte;
591 unset($contexte[$champ_parent]);
592 $args = http_build_query($contexte);
597 if ($propre === false) {
599 } // objet inconnu. raccourci ?
602 $url = _debut_urls_arbo
604 . rtrim($propre, '/')
605 . url_arbo_terminaison($type);
607 // objet connu mais sans possibilite d'URL lisible, revenir au defaut
608 include_spip('base/connect_sql');
609 $id_type = id_table_objet($type);
610 $url = get_spip_script('./') . '?' . _SPIP_PAGE
. "=$type&$id_type=$id";
615 $url .= ((strpos($url, '?') === false) ?
'?' : '&') . $args;
623 return _DIR_RACINE
. $url;
628 * API : retourner l'url d'un objet si i est numerique
629 * ou decoder cette url si c'est une chaine
630 * array([contexte],[type],[url_redirect],[fond]) : url decodee
632 * https://code.spip.net/@urls_arbo_dist
634 * @param string|int $i
635 * @param string $entite
636 * @param string|array $args
637 * @param string $ancre
638 * @return array|string
640 function urls_arbo_dist($i, $entite, $args = '', $ancre = '') {
641 if (is_numeric($i)) {
642 return _generer_url_arbo($entite, $i, $args, $ancre);
645 // traiter les injections du type domaine.org/spip.php/cestnimportequoi/ou/encore/plus/rubrique23
646 if ($GLOBALS['profondeur_url'] > 0 and $entite == 'sommaire') {
647 $entite = 'type_urls';
650 // recuperer les &debut_xx;
651 if (is_array($args)) {
653 $args = http_build_query($contexte);
655 parse_str($args, $contexte);
659 $id_objet = $type = 0;
660 $url_redirect = null;
662 // Migration depuis anciennes URLs ?
663 // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
664 if ($GLOBALS['profondeur_url'] <= 0
665 and $_SERVER['REQUEST_METHOD'] != 'POST'
667 include_spip('inc/urls');
668 $r = nettoyer_url_page($i, $contexte);
670 list($contexte, $type, , , $suite) = $r;
671 $_id = id_table_objet($type);
672 $id_objet = $contexte[$_id];
673 $url_propre = generer_url_entite($id_objet, $type);
674 if (strlen($url_propre)
675 and !strstr($url, $url_propre)
677 objet_test_si_publie($type, $id_objet)
678 OR (defined('_VAR_PREVIEW') and _VAR_PREVIEW
and autoriser('voir', $type, $id_objet))
681 list(, $hash) = array_pad(explode('#', $url_propre), 2, null);
683 foreach (array_filter(explode('&', $suite)) as $fragment) {
684 if ($fragment != "$_id=$id_objet") {
688 $url_redirect = generer_url_entite($id_objet, $type, join('&', array_filter($args)), $hash);
690 return array($contexte, $type, $url_redirect, $type);
694 /* Fin compatibilite anciennes urls */
696 // Chercher les valeurs d'environnement qui indiquent l'url-propre
697 $url_propre = preg_replace(',[?].*,', '', $url);
699 // Mode Query-String ?
701 and preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)
707 or $url_propre == _DIR_RESTREINT_ABS
708 or $url_propre == _SPIP_SCRIPT
711 } // qu'est-ce qu'il veut ???
714 include_spip('base/abstract_sql'); // chercher dans la table des URLS
716 // Revenir en utf-8 si encodage type %D8%A7 (farsi)
717 $url_propre = rawurldecode($url_propre);
719 // Compatibilite avec .htm/.html et autres terminaisons
720 $t = array_diff(array_unique(array_merge(array('.html', '.htm', '/'), url_arbo_terminaison(''))), array(''));
722 $url_propre = preg_replace('{('
723 . implode('|', array_map('preg_quote', $t)) . ')$}i', '', $url_propre);
726 if (strlen($url_propre) and !preg_match(',^[^/]*[.]php,', $url_propre)) {
727 $parents_vus = array();
729 // recuperer tous les objets de larbo xxx/article/yyy/mot/zzzz
730 // on parcourt les segments de gauche a droite
731 // pour pouvoir contextualiser un segment par son parent
732 $url_arbo = explode('/', $url_propre);
733 $url_arbo_new = array();
734 $dernier_parent_vu = false;
738 if (_url_arbo_multilang
=== true) {
739 // la langue : si fourni en QS prioritaire car vient du skel ou de forcer_lang
740 if (isset($contexte['lang'])) {
741 $langue = $contexte['lang'];
743 // le premier segment peut etre la langue : l'extraire
744 // on le prend en compte si lang non fournie par la QS sinon on l'ignore
745 include_spip('action/editer_url'); // pour url_verifier_langue
746 if (count($url_arbo) > 1
747 and $first = reset($url_arbo)
748 and url_verifier_langue($first)) {
749 array_shift($url_arbo);
751 $contexte['lang'] = $langue = $first;
754 } elseif (_url_arbo_multilang
) {
755 $langue = _url_arbo_multilang
;
758 while (count($url_arbo) > 0) {
760 if (count($url_arbo) > 1) {
761 $type = array_shift($url_arbo);
763 $url_segment = array_shift($url_arbo);
764 // Rechercher le segment de candidat
765 // si on est dans un contexte de parent, donne par le segment precedent,
766 // prefixer le segment recherche avec ce contexte
767 $cp = '0'; // par defaut : parent racine, id=0
768 if ($dernier_parent_vu) {
769 $cp = $parents_vus[$dernier_parent_vu];
771 // d'abord recherche avec prefixe parent, en une requete car aucun risque de colision
773 'id_objet, type, url',
776 ?
'url=' . sql_quote($url_segment, '', 'TEXT')
777 : sql_in('url', array("$type/$url_segment", $type)),
779 // en priorite celui qui a le bon parent
780 // puis la bonne langue puis la langue ''
781 // puis les deux segments puis 1 seul segment
783 // si parent indefini on privilegie id_parent=0 avec la derniere clause du order
784 (intval($cp) ?
'id_parent=' . intval($cp) . ' DESC, ' : 'id_parent>=0 DESC, ')
785 . ($langue?
'langue='.sql_quote($langue).' DESC, ':'') ."langue='' DESC,"
786 . 'segments DESC, id_parent'
789 if (!is_null($type) and $row['url'] == $type) {
790 array_unshift($url_arbo, $url_segment);
791 $url_segment = $type;
794 $type = $row['type'];
795 $col_id = id_table_objet($type);
797 // le plus a droite l'emporte pour des objets presents plusieurs fois dans l'url (ie rubrique)
798 $contexte[$col_id] = $row['id_objet'];
801 if ($p = url_arbo_parent($type)) {
802 $type_parent = end($p);
804 // l'entite la plus a droite l'emporte, si le type de son parent a ete vu
805 // sinon c'est un segment contextuel supplementaire a ignorer
806 // ex : rub1/article/art1/mot1 : il faut ignorer le mot1, la vrai url est celle de l'article
808 or $dernier_parent_vu == $type_parent
810 if ($objet_segments == 0) {
813 } // sinon on change d'objet concerne
818 $url_arbo_new[$objet_segments]['id_objet'] = $row['id_objet'];
819 $url_arbo_new[$objet_segments]['objet'] = $type;
820 $url_arbo_new[$objet_segments]['segment'][] = $row['url'];
822 // on note le dernier parent vu de chaque type
823 $parents_vus[$dernier_parent_vu = $type] = $row['id_objet'];
825 // un segment est inconnu
826 if ($entite == '' or $entite == 'type_urls') {
827 // on genere une 404 comme il faut si on ne sait pas ou aller
828 return array(array(), '404');
830 // ici on a bien reconnu un segment en amont, mais le segment en cours est inconnu
831 // on pourrait renvoyer sur le dernier segment identifie
832 // mais de fait l'url entiere est inconnu : 404 aussi
833 // mais conserver le contexte qui peut contenir un fond d'ou venait peut etre $entite (reecriture urls)
834 return array($contexte, '404');
838 if (count($url_arbo_new)) {
839 $caller = debug_backtrace();
840 $caller = $caller[1]['function'];
841 // si on est appele par un autre module d'url c'est du decodage d'une ancienne URL
842 // ne pas regenerer des segments arbo, mais rediriger vers la nouvelle URL
843 // dans la nouvelle forme
844 if (strncmp($caller, 'urls_', 5) == 0 and $caller !== 'urls_decoder_url') {
845 // en absolue, car assembler ne gere pas ce cas particulier
846 include_spip('inc/filtres_mini');
847 $col_id = id_table_objet($entite);
848 $url_new = generer_url_entite($contexte[$col_id], $entite, $args);
849 // securite contre redirection infinie
850 if ($url_new !== $url_propre
851 and rtrim($url_new, '/') !== rtrim($url_propre, '/')
853 $url_redirect = url_absolue($url_new);
856 foreach ($url_arbo_new as $k => $o) {
857 $c = array( 'langue' => $langue );
858 if (isset($parents_vus['rubrique'])) {
859 $c['id_parent'] = $parents_vus['rubrique'];
861 if ($s = declarer_url_arbo($o['objet'], $o['id_objet'], $c)) {
862 $url_arbo_new[$k] = $s;
864 $url_arbo_new[$k] = implode('/', $o['segment']);
867 $url_arbo_new = ltrim(implode('/', $url_arbo_new), '/');
868 if ($langue and _url_arbo_multilang
=== true) {
869 $url_arbo_new = "$langue/" . $url_arbo_new;
870 if (strpos($args, 'lang=') !== false) {
871 parse_str($args, $cl);
873 $args = http_build_query($cl);
876 if ($url_arbo_new !== $url_propre) {
877 //var_dump($url_arbo_new,$url_propre);
878 $url_redirect = _debut_urls_arbo
880 . url_arbo_terminaison($entite)
881 . ($args?
"?$args":'');
882 // en absolue, car assembler ne gere pas ce cas particulier
883 include_spip('inc/filtres_mini');
884 $url_redirect = url_absolue($url_redirect);
889 // gerer le retour depuis des urls propres
890 if (($entite == '' or $entite == 'type_urls')
891 and $GLOBALS['profondeur_url'] <= 0
893 $urls_anciennes = charger_fonction('propres', 'urls');
895 return $urls_anciennes($url_propre, $entite, $contexte);
898 if ($entite == '' or $entite == 'type_urls' /* compat .htaccess 2.0 */) {
900 $entite = objet_type($type);
902 // Si ca ressemble a une URL d'objet, ce n'est pas la home
903 // et on provoque un 404
904 if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
906 $contexte['erreur'] = ''; // qu'afficher ici ? l'url n'existe pas... on ne sait plus dire de quel type d'objet il s'agit
910 if (!defined('_SET_HTML_BASE')) {
911 define('_SET_HTML_BASE', 1);
914 return array($contexte, $entite, $url_redirect, null);