arbo.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. <?php
  2. /***************************************************************************\
  3. * SPIP, Systeme de publication pour l'internet *
  4. * *
  5. * Copyright (c) 2001-2014 *
  6. * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
  7. * *
  8. * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
  9. * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
  10. \***************************************************************************/
  11. if (!defined("_ECRIRE_INC_VERSION")) return; // securiser
  12. # donner un exemple d'url pour le formulaire de choix
  13. define('URLS_ARBO_EXEMPLE', '/article/titre');
  14. # specifier le form de config utilise pour ces urls
  15. define('URLS_ARBO_CONFIG', 'arbo');
  16. // TODO: une interface permettant de verifier qu'on veut effectivment modifier
  17. // une adresse existante
  18. defined('CONFIRMER_MODIFIER_URL') || define('CONFIRMER_MODIFIER_URL', false);
  19. /**
  20. * - Comment utiliser ce jeu d'URLs ?
  21. * Recopiez le fichier "htaccess.txt" du repertoire de base du site SPIP sous
  22. * le sous le nom ".htaccess" (attention a ne pas ecraser d'autres reglages
  23. * que vous pourriez avoir mis dans ce fichier) ; si votre site est en
  24. * "sous-repertoire", vous devrez aussi editer la ligne "RewriteBase" ce fichier.
  25. * Les URLs definies seront alors redirigees vers les fichiers de SPIP.
  26. *
  27. * Choisissez "arbo" dans les pages de configuration d'URL
  28. *
  29. * SPIP calculera alors ses liens sous la forme "Mon-titre-d-article".
  30. * Variantes :
  31. *
  32. * Terminaison :
  33. * les terminaisons ne *sont pas* stockees en base, elles servent juste
  34. * a rendre les url jolies ou conformes a un usage
  35. * pour avoir des url terminant par html
  36. * define ('_terminaison_urls_arbo', '.html');
  37. *
  38. * pour preciser des terminaisons particulieres pour certains types
  39. * $GLOBALS['url_arbo_terminaisons']=array(
  40. * 'rubrique' => '/',
  41. * 'mot' => '',
  42. * 'groupe' => '/',
  43. * 'defaut' => '.html');
  44. *
  45. * pour avoir des url numeriques (id) du type 12/5/4/article/23
  46. * define ('_URLS_ARBO_MIN',255);
  47. *
  48. *
  49. * pour conserver la casse des titres dans les url
  50. * define ('_url_arbo_minuscules',0);
  51. *
  52. * pour choisir le caractere de separation titre-id en cas de doublon
  53. * (ne pas utiliser '/')
  54. * define ('_url_arbo_sep_id','-');
  55. *
  56. * pour modifier la hierarchie apparente dans la constitution des urls
  57. * ex pour que les mots soient classes par groupes
  58. * $GLOBALS['url_arbo_parents']=array(
  59. * 'article'=>array('id_rubrique','rubrique'),
  60. * 'rubrique'=>array('id_parent','rubrique'),
  61. * 'breve'=>array('id_rubrique','rubrique'),
  62. * 'site'=>array('id_rubrique','rubrique'),
  63. * 'mot'=>array('id_groupe','groupes_mot'));
  64. *
  65. * pour personaliser les types
  66. * $GLOBALS['url_arbo_types']=array(
  67. * 'rubrique'=>'', // pas de type pour les rubriques
  68. * 'article'=>'a',
  69. * 'mot'=>'tags'
  70. * );
  71. *
  72. */
  73. $config_urls_arbo = isset($GLOBALS['meta']['urls_arbo'])?unserialize($GLOBALS['meta']['urls_arbo']):array();
  74. if (!defined('_debut_urls_arbo')) define('_debut_urls_arbo', '');
  75. if (!defined('_terminaison_urls_arbo')) define('_terminaison_urls_arbo', '');
  76. // pour choisir le caractere de separation titre-id en cas de doublon
  77. // (ne pas utiliser '/')
  78. if (!defined('_url_arbo_sep_id')) define('_url_arbo_sep_id',isset($config_urls_arbo['url_arbo_sep_id'])?$config_urls_arbo['url_arbo_sep_id']:'-');
  79. // option pour tout passer en minuscules
  80. if (!defined('_url_arbo_minuscules')) define('_url_arbo_minuscules',isset($config_urls_arbo['url_arbo_minuscules'])?$config_urls_arbo['url_arbo_minuscules']:1);
  81. if (!defined('_URLS_ARBO_MAX')) define('_URLS_ARBO_MAX', isset($config_urls_arbo['URLS_ARBO_MAX'])?$config_urls_arbo['URLS_ARBO_MAX']:80);
  82. if (!defined('_URLS_ARBO_MIN')) define('_URLS_ARBO_MIN', isset($config_urls_arbo['URLS_ARBO_MIN'])?$config_urls_arbo['URLS_ARBO_MIN']:3);
  83. if (!defined('_url_sep_id')) define('_url_sep_id',_url_arbo_sep_id);
  84. // Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
  85. // indiquer la table ou les chercher (articles, auteurs etc),
  86. // et elles etaient retirees par les preg_match dans la fonction ci-dessous.
  87. // Elles sont a present definies a "" pour avoir des URL plus jolies
  88. // mais les preg_match restent necessaires pour gerer les anciens signets.
  89. #define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
  90. if (!defined('_MARQUEUR_URL')) define('_MARQUEUR_URL', false);
  91. /**
  92. * Definir les parentees utilisees pour construire des urls arborescentes
  93. *
  94. * @param string $type
  95. * @return string
  96. */
  97. function url_arbo_parent($type){
  98. static $parents = null;
  99. if (is_null($parents)){
  100. $parents = array(
  101. 'article'=>array('id_rubrique','rubrique'),
  102. 'rubrique'=>array('id_parent','rubrique'),
  103. 'breve'=>array('id_rubrique','rubrique'),
  104. 'site'=>array('id_rubrique','rubrique'));
  105. if (isset($GLOBALS['url_arbo_parents']) AND !isset($_REQUEST['url_arbo_parents'])){
  106. $parents = array_merge($parents,$GLOBALS['url_arbo_parents']);
  107. }
  108. }
  109. return (isset($parents[$type])?$parents[$type]:'');
  110. }
  111. /**
  112. * Definir les terminaisons des urls :
  113. * / pour une rubrique
  114. * .html pour une page etc..
  115. *
  116. * @param string $type
  117. * @return string
  118. */
  119. function url_arbo_terminaison($type){
  120. static $terminaison_types = null;
  121. if ($terminaison_types==null){
  122. $terminaison_types = array('rubrique' => '/','mot' => '','defaut' => defined('_terminaison_urls_arbo')?_terminaison_urls_arbo:'.html');
  123. if (isset($GLOBALS['url_arbo_terminaisons']))
  124. $terminaison_types = array_merge($terminaison_types,$GLOBALS['url_arbo_terminaisons']);
  125. }
  126. // si c'est un appel avec type='' c'est pour avoir la liste des terminaisons
  127. if (!$type)
  128. return array_unique(array_values($terminaison_types));
  129. if (isset($terminaison_types[$type]))
  130. return $terminaison_types[$type];
  131. elseif (isset($terminaison_types['defaut']))
  132. return $terminaison_types['defaut'];
  133. return "";
  134. }
  135. /**
  136. * Definir le prefixe qui designe le type et qu'on utilise pour chaque objet
  137. * ex : "article"/truc
  138. * par defaut les rubriques ne sont pas typees, mais le reste oui
  139. *
  140. * @param string $type
  141. * @return array|string
  142. */
  143. function url_arbo_type($type){
  144. static $synonymes_types = null;
  145. if (!$synonymes_types){
  146. $synonymes_types = array('rubrique'=>'');
  147. if (isset($GLOBALS['url_arbo_types']) AND is_array($GLOBALS['url_arbo_types']))
  148. $synonymes_types = array_merge($synonymes_types,$GLOBALS['url_arbo_types']);
  149. }
  150. // si c'est un appel avec type='' c'est pour avoir la liste inversee des synonymes
  151. if (!$type)
  152. return array_flip($synonymes_types);
  153. return
  154. ($t=(isset($synonymes_types[$type])?$synonymes_types[$type]:$type)) // le type ou son synonyme
  155. . ($t?'/':''); // le / eventuel pour separer, si le synonyme n'est pas vide
  156. }
  157. /**
  158. * Pipeline pour creation d'une adresse : il recoit l'url propose par le
  159. * precedent, un tableau indiquant le titre de l'objet, son type, son id,
  160. * et doit donner en retour une chaine d'url, sans se soucier de la
  161. * duplication eventuelle, qui sera geree apres
  162. * http://code.spip.net/@creer_chaine_url
  163. *
  164. * @param array $x
  165. * @return array
  166. */
  167. function urls_arbo_creer_chaine_url($x) {
  168. // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
  169. // pourrait l'utiliser pour juste ajouter la
  170. $url_old = $x['data'];
  171. $objet = $x['objet'];
  172. include_spip('inc/filtres');
  173. include_spip('action/editer_url');
  174. if (!$url = url_nettoyer($objet['titre'],_URLS_ARBO_MAX,_URLS_ARBO_MIN,'-',_url_arbo_minuscules?'strtolower':''))
  175. $url = $objet['id_objet'];
  176. $x['data'] =
  177. url_arbo_type($objet['type']) // le type ou son synonyme
  178. . $url; // le titre
  179. return $x;
  180. }
  181. /**
  182. * Boucler sur le parent pour construire l'url complete a partir des segments
  183. * http://code.spip.net/@declarer_url_arbo_rec
  184. *
  185. * @param string $url
  186. * @param string $type
  187. * @param string $parent
  188. * @param string $type_parent
  189. * @return string
  190. */
  191. function declarer_url_arbo_rec($url,$type,$parent,$type_parent){
  192. if (is_null($parent)){
  193. return $url;
  194. }
  195. // Si pas de parent ou si son URL est vide, on ne renvoit que l'URL de l'objet en court
  196. if ($parent==0 or !($url_parent = declarer_url_arbo($type_parent?$type_parent:'rubrique',$parent))){
  197. return rtrim($url,'/');
  198. }
  199. // Sinon on renvoit l'URL de l'objet concaténée avec celle du parent
  200. else {
  201. return rtrim($url_parent,'/') . '/' . rtrim($url,'/');
  202. }
  203. }
  204. /**
  205. * Renseigner les infos les plus recentes de l'url d'un objet
  206. * et de quoi la (re)construire si besoin
  207. * @param string $type
  208. * @param int $id_objet
  209. * @return bool|null|array
  210. */
  211. function renseigner_url_arbo($type,$id_objet){
  212. $urls = array();
  213. $trouver_table = charger_fonction('trouver_table', 'base');
  214. $desc = $trouver_table(table_objet($type));
  215. $table = $desc['table'];
  216. $col_id = @$desc['key']["PRIMARY KEY"];
  217. if (!$col_id) return false; // Quand $type ne reference pas une table
  218. $id_objet = intval($id_objet);
  219. $champ_titre = $desc['titre'] ? $desc['titre'] : 'titre';
  220. // parent
  221. $champ_parent = url_arbo_parent($type);
  222. $sel_parent = ', 0 as parent';
  223. $order_by_parent = "";
  224. if ($champ_parent){
  225. $sel_parent = ", O.".reset($champ_parent).' as parent';
  226. // trouver l'url qui matche le parent en premier
  227. $order_by_parent = "O.".reset($champ_parent)."=U.id_parent DESC, ";
  228. }
  229. // Recuperer une URL propre correspondant a l'objet.
  230. $row = sql_fetsel("U.url, U.date, U.id_parent, U.perma, $champ_titre $sel_parent",
  231. "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)",
  232. "O.$col_id=$id_objet",
  233. '',
  234. $order_by_parent.'U.perma DESC, U.date DESC', 1);
  235. if ($row){
  236. $urls[$type][$id_objet] = $row;
  237. $urls[$type][$id_objet]['type_parent'] = $champ_parent?end($champ_parent):'';
  238. }
  239. return isset($urls[$type][$id_objet])?$urls[$type][$id_objet]:null;
  240. }
  241. /**
  242. * Retrouver/Calculer l'ensemble des segments d'url d'un objet
  243. *
  244. * http://code.spip.net/@declarer_url_arbo
  245. *
  246. * @param string $type
  247. * @param int $id_objet
  248. * @return string
  249. */
  250. function declarer_url_arbo($type, $id_objet) {
  251. static $urls=array();
  252. // Se contenter de cette URL si elle existe ;
  253. // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
  254. // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
  255. // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
  256. // qui requetent en base
  257. $modifier_url = (defined('_VAR_URLS') AND _VAR_URLS);
  258. if (!isset($urls[$type][$id_objet]) OR $modifier_url) {
  259. $r = renseigner_url_arbo($type,$id_objet);
  260. // Quand $type ne reference pas une table
  261. if ($r===false)
  262. return false;
  263. if (!is_null($r))
  264. $urls[$type][$id_objet] = $r;
  265. }
  266. if (!isset($urls[$type][$id_objet])) return ""; # objet inexistant
  267. $url_propre = $urls[$type][$id_objet]['url'];
  268. // si on a trouve l'url
  269. // et que le parent est bon
  270. // et (permanente ou pas de demande de modif)
  271. if (!is_null($url_propre)
  272. AND $urls[$type][$id_objet]['id_parent'] == $urls[$type][$id_objet]['parent']
  273. AND ($urls[$type][$id_objet]['perma'] OR !$modifier_url))
  274. return declarer_url_arbo_rec($url_propre,$type,
  275. isset($urls[$type][$id_objet]['parent'])?$urls[$type][$id_objet]['parent']:0,
  276. isset($urls[$type][$id_objet]['type_parent'])?$urls[$type][$id_objet]['type_parent']:null);
  277. // Si URL inconnue ou maj forcee sur une url non permanente, recreer une url
  278. $url = $url_propre;
  279. if (is_null($url_propre) OR ($modifier_url AND !$urls[$type][$id_objet]['perma'])) {
  280. $url = pipeline('arbo_creer_chaine_url',
  281. array(
  282. 'data' => $url_propre, // le vieux url_propre
  283. 'objet' => array_merge($urls[$type][$id_objet],
  284. array('type' => $type, 'id_objet' => $id_objet)
  285. )
  286. )
  287. );
  288. // Eviter de tamponner les URLs a l'ancienne (cas d'un article
  289. // intitule "auteur2")
  290. include_spip('inc/urls');
  291. $objets = urls_liste_objets();
  292. if (preg_match(',^('.$objets.')[0-9]*$,', $url, $r)
  293. AND $r[1] != $type)
  294. $url = $url._url_arbo_sep_id.$id_objet;
  295. }
  296. // Pas de changement d'url ni de parent
  297. if ($url == $url_propre
  298. AND $urls[$type][$id_objet]['id_parent'] == $urls[$type][$id_objet]['parent'])
  299. return declarer_url_arbo_rec($url_propre,$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
  300. // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
  301. if ($modifier_url) {
  302. include_spip('inc/autoriser');
  303. $modifier_url = autoriser('modifierurl', $type, $id_objet);
  304. }
  305. // Verifier si l'utilisateur veut effectivement changer l'URL
  306. if ($modifier_url
  307. AND CONFIRMER_MODIFIER_URL
  308. AND $url_propre
  309. // on essaye pas de regenerer une url en -xxx (suffixe id anti collision)
  310. AND $url != preg_replace('/'.preg_quote(_url_propres_sep_id,'/').'.*/', '', $url_propre))
  311. $confirmer = true;
  312. else
  313. $confirmer = false;
  314. if ($confirmer AND !_request('ok')) {
  315. die ("vous changez d'url ? $url_propre -&gt; $url");
  316. }
  317. $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet, 'id_parent'=>$urls[$type][$id_objet]['parent'],'perma'=>intval($urls[$type][$id_objet]['perma']));
  318. include_spip('action/editer_url');
  319. if (url_insert($set,$confirmer,_url_arbo_sep_id)){
  320. $urls[$type][$id_objet]['url'] = $set['url'];
  321. $urls[$type][$id_objet]['id_parent'] = $set['id_parent'];
  322. }
  323. else {
  324. // l'insertion a echoue,
  325. //serveur out ? retourner au mieux
  326. $urls[$type][$id_objet]['url']=$url_propre;
  327. }
  328. return declarer_url_arbo_rec($urls[$type][$id_objet]['url'],$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
  329. }
  330. /**
  331. * Generer l'url arbo complete constituee des segments + debut + fin
  332. *
  333. * http://code.spip.net/@_generer_url_arbo
  334. *
  335. * @param string $type
  336. * @param int $id
  337. * @param string $args
  338. * @param string $ancre
  339. * @return string
  340. */
  341. function _generer_url_arbo($type, $id, $args='', $ancre='') {
  342. if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
  343. $url = $generer_url_externe($id, $args, $ancre);
  344. if (NULL != $url) return $url;
  345. }
  346. // Mode propre
  347. $propre = declarer_url_arbo($type, $id);
  348. if ($propre === false) return ''; // objet inconnu. raccourci ?
  349. if ($propre) {
  350. $url = _debut_urls_arbo
  351. . rtrim($propre,'/')
  352. . url_arbo_terminaison($type);
  353. } else {
  354. // objet connu mais sans possibilite d'URL lisible, revenir au defaut
  355. include_spip('base/connect_sql');
  356. $id_type = id_table_objet($type);
  357. $url = get_spip_script('./')."?"._SPIP_PAGE."=$type&$id_type=$id";
  358. }
  359. // Ajouter les args
  360. if ($args)
  361. $url .= ((strpos($url, '?')===false) ? '?' : '&') . $args;
  362. // Ajouter l'ancre
  363. if ($ancre)
  364. $url .= "#$ancre";
  365. return _DIR_RACINE . $url;
  366. }
  367. /**
  368. * API : retourner l'url d'un objet si i est numerique
  369. * ou decoder cette url si c'est une chaine
  370. * array([contexte],[type],[url_redirect],[fond]) : url decodee
  371. *
  372. * http://code.spip.net/@urls_arbo_dist
  373. *
  374. * @param string|int $i
  375. * @param string $entite
  376. * @param string|array $args
  377. * @param string $ancre
  378. * @return array|string
  379. */
  380. function urls_arbo_dist($i, $entite, $args='', $ancre='') {
  381. if (is_numeric($i))
  382. return _generer_url_arbo($entite, $i, $args, $ancre);
  383. // traiter les injections du type domaine.org/spip.php/cestnimportequoi/ou/encore/plus/rubrique23
  384. if ($GLOBALS['profondeur_url']>0 AND $entite=='sommaire'){
  385. $entite = 'type_urls';
  386. }
  387. // recuperer les &debut_xx;
  388. if (is_array($args))
  389. $contexte = $args;
  390. else
  391. parse_str($args,$contexte);
  392. $url = $i;
  393. $id_objet = $type = 0;
  394. $url_redirect = null;
  395. // Migration depuis anciennes URLs ?
  396. // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
  397. if ($GLOBALS['profondeur_url']<=0
  398. AND $_SERVER['REQUEST_METHOD'] != 'POST') {
  399. include_spip('inc/urls');
  400. $r = nettoyer_url_page($i, $contexte);
  401. if ($r) {
  402. list($contexte, $type,,, $suite) = $r;
  403. $_id = id_table_objet($type);
  404. $id_objet = $contexte[$_id];
  405. $url_propre = generer_url_entite($id_objet, $type);
  406. if (strlen($url_propre)
  407. AND !strstr($url,$url_propre)) {
  408. list(,$hash) = explode('#', $url_propre);
  409. $args = array();
  410. foreach(array_filter(explode('&', $suite)) as $fragment) {
  411. if ($fragment != "$_id=$id_objet")
  412. $args[] = $fragment;
  413. }
  414. $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
  415. return array($contexte, $type, $url_redirect, $type);
  416. }
  417. }
  418. }
  419. /* Fin compatibilite anciennes urls */
  420. // Chercher les valeurs d'environnement qui indiquent l'url-propre
  421. if (isset($_SERVER['REDIRECT_url_propre']))
  422. $url_propre = $_SERVER['REDIRECT_url_propre'];
  423. elseif (isset($_ENV['url_propre']))
  424. $url_propre = $_ENV['url_propre'];
  425. else {
  426. // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
  427. $url = ltrim($url,'/');
  428. $url = explode('/',$url);
  429. while (count($url)>$GLOBALS['profondeur_url']+1)
  430. array_shift($url);
  431. $url = implode('/',$url);
  432. $url_propre = preg_replace(',[?].*,', '', $url);
  433. }
  434. // Mode Query-String ?
  435. if (!$url_propre
  436. AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
  437. $url_propre = $r[1];
  438. }
  439. if (!$url_propre
  440. OR $url_propre==_DIR_RESTREINT_ABS
  441. OR $url_propre==_SPIP_SCRIPT) return; // qu'est-ce qu'il veut ???
  442. include_spip('base/abstract_sql'); // chercher dans la table des URLS
  443. // Revenir en utf-8 si encodage type %D8%A7 (farsi)
  444. $url_propre = rawurldecode($url_propre);
  445. // Compatibilite avec .htm/.html et autres terminaisons
  446. $t = array_diff(array_unique(array_merge(array('.html','.htm','/'),url_arbo_terminaison(''))),array(''));
  447. if (count($t))
  448. $url_propre = preg_replace('{('
  449. .implode('|',array_map('preg_quote',$t)).')$}i', '', $url_propre);
  450. if (strlen($url_propre) AND !preg_match(',^[^/]*[.]php,',$url_propre)){
  451. $parents_vus = array();
  452. // recuperer tous les objets de larbo xxx/article/yyy/mot/zzzz
  453. // on parcourt les segments de gauche a droite
  454. // pour pouvoir contextualiser un segment par son parent
  455. $url_arbo = explode('/',$url_propre);
  456. $url_arbo_new = array();
  457. $dernier_parent_vu = false;
  458. $objet_segments = 0;
  459. while (count($url_arbo)>0){
  460. $type=null;
  461. if (count($url_arbo)>1)
  462. $type = array_shift($url_arbo);
  463. $url_segment = array_shift($url_arbo);
  464. // Rechercher le segment de candidat
  465. // si on est dans un contexte de parent, donne par le segment precedent,
  466. // prefixer le segment recherche avec ce contexte
  467. $cp = "0"; // par defaut : parent racine, id=0
  468. if ($dernier_parent_vu)
  469. $cp = $parents_vus[$dernier_parent_vu];
  470. // d'abord recherche avec prefixe parent, en une requete car aucun risque de colision
  471. $row=sql_fetsel('id_objet, type, url',
  472. 'spip_urls',
  473. is_null($type)
  474. ? "url=".sql_quote($url_segment, '', 'TEXT')
  475. : sql_in('url',array("$type/$url_segment",$type)),
  476. '',
  477. // en priorite celui qui a le bon parent et les deux segments
  478. // puis le bon parent avec 1 segment
  479. // puis un parent indefini (le 0 de preference) et les deux segments
  480. // puis un parent indefini (le 0 de preference) et 1 segment
  481. (intval($cp)?"id_parent=".intval($cp)." DESC, ":"")."segments DESC, id_parent"
  482. );
  483. if ($row){
  484. if (!is_null($type) AND $row['url']==$type){
  485. array_unshift($url_arbo,$url_segment);
  486. $url_segment = $type;
  487. $type = null;
  488. }
  489. $type = $row['type'];
  490. $col_id = id_table_objet($type);
  491. // le plus a droite l'emporte pour des objets presents plusieurs fois dans l'url (ie rubrique)
  492. $contexte[$col_id] = $row['id_objet'];
  493. $type_parent = '';
  494. if ($p = url_arbo_parent($type))
  495. $type_parent=end($p);
  496. // l'entite la plus a droite l'emporte, si le type de son parent a ete vu
  497. // sinon c'est un segment contextuel supplementaire a ignorer
  498. // ex : rub1/article/art1/mot1 : il faut ignorer le mot1, la vrai url est celle de l'article
  499. if (!$entite
  500. OR $dernier_parent_vu == $type_parent){
  501. if ($objet_segments==0)
  502. $entite = $type;
  503. }
  504. // sinon on change d'objet concerne
  505. else{
  506. $objet_segments++;
  507. }
  508. $url_arbo_new[$objet_segments]['id_objet'] = $row['id_objet'];
  509. $url_arbo_new[$objet_segments]['objet'] = $type;
  510. $url_arbo_new[$objet_segments]['segment'][] = $row['url'];
  511. // on note le dernier parent vu de chaque type
  512. $parents_vus[$dernier_parent_vu = $type] = $row['id_objet'];
  513. }
  514. else {
  515. // un segment est inconnu
  516. if ($entite=='' OR $entite=='type_urls') {
  517. // on genere une 404 comme il faut si on ne sait pas ou aller
  518. return array(array(),'404');
  519. }
  520. // ici on a bien reconnu un segment en amont, mais le segment en cours est inconnu
  521. // on pourrait renvoyer sur le dernier segment identifie
  522. // mais de fait l'url entiere est inconnu : 404 aussi
  523. // mais conserver le contexte qui peut contenir un fond d'ou venait peut etre $entite (reecriture urls)
  524. return array($contexte,'404');
  525. }
  526. }
  527. if (count($url_arbo_new)){
  528. $caller = debug_backtrace();
  529. $caller = $caller[1]['function'];
  530. // si on est appele par un autre module d'url c'est du decodage d'une ancienne URL
  531. // ne pas regenerer des segments arbo, mais rediriger vers la nouvelle URL
  532. // dans la nouvelle forme
  533. if (strncmp($caller,"urls_",5)==0 AND $caller!=="urls_decoder_url"){
  534. // en absolue, car assembler ne gere pas ce cas particulier
  535. include_spip('inc/filtres_mini');
  536. $col_id = id_table_objet($entite);
  537. $url_new = generer_url_entite($contexte[$col_id],$entite);
  538. // securite contre redirection infinie
  539. if ($url_new!==$url_propre
  540. AND rtrim($url_new,"/")!==rtrim($url_propre,"/"))
  541. $url_redirect = url_absolue($url_new);
  542. }
  543. else {
  544. foreach($url_arbo_new as $k=>$o)
  545. if ($s = declarer_url_arbo($o['objet'],$o['id_objet']))
  546. $url_arbo_new[$k] = $s;
  547. else
  548. $url_arbo_new[$k] = implode('/',$o['segment']);
  549. $url_arbo_new = ltrim(implode('/',$url_arbo_new),'/');
  550. if ($url_arbo_new!==$url_propre){
  551. $url_redirect = $url_arbo_new;
  552. // en absolue, car assembler ne gere pas ce cas particulier
  553. include_spip('inc/filtres_mini');
  554. $url_redirect = url_absolue($url_redirect);
  555. }
  556. }
  557. }
  558. // gerer le retour depuis des urls propres
  559. if (($entite=='' OR $entite=='type_urls')
  560. AND $GLOBALS['profondeur_url']<=0){
  561. $urls_anciennes = charger_fonction('propres','urls');
  562. return $urls_anciennes($url_propre, $entite, $contexte);
  563. }
  564. }
  565. if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
  566. if ($type)
  567. $entite = objet_type ($type);
  568. else {
  569. // Si ca ressemble a une URL d'objet, ce n'est pas la home
  570. // et on provoque un 404
  571. if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
  572. $entite = '404';
  573. $contexte['erreur'] = ''; // qu'afficher ici ? l'url n'existe pas... on ne sait plus dire de quel type d'objet il s'agit
  574. }
  575. }
  576. }
  577. define('_SET_HTML_BASE',1);
  578. return array($contexte, $entite, $url_redirect, null);
  579. }
  580. ?>