propres.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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_PROPRES_EXEMPLE', 'Titre-de-l-article -Rubrique-');
  14. # specifier le form de config utilise pour ces urls
  15. define('URLS_PROPRES_CONFIG', 'propres');
  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. Dans les pages de configuration, choisissez 'propres' comme type d'url
  27. SPIP calculera alors ses liens sous la forme
  28. "Mon-titre-d-article".
  29. La variante 'propres2' ajoutera '.html' aux adresses generees :
  30. "Mon-titre-d-article.html"
  31. Variante 'qs' (experimentale) : ce systeme fonctionne en "Query-String",
  32. c'est-a-dire sans utilisation de .htaccess ; les adresses sont de la forme
  33. "/?Mon-titre-d-article"
  34. */
  35. if (!defined('_terminaison_urls_propres')) define ('_terminaison_urls_propres', '');
  36. if (!defined('_debut_urls_propres')) define ('_debut_urls_propres', '');
  37. $config_urls_propres = isset($GLOBALS['meta']['urls_propres'])?unserialize($GLOBALS['meta']['urls_propres']):array();
  38. // pour choisir le caractere de separation titre-id en cas de doublon
  39. // (ne pas utiliser '/')
  40. if (!defined('_url_propres_sep_id')) define('_url_propres_sep_id',isset($config_urls_propres['url_propres_sep_id'])?$config_urls_propres['url_propres_sep_id']:'-');
  41. // option pour tout passer en minuscules
  42. if (!defined('_url_minuscules')) define('_url_minuscules',isset($config_urls_propres['url_minuscules'])?$config_urls_propres['url_minuscules']:0);
  43. if (!defined('_URLS_PROPRES_MAX')) define('_URLS_PROPRES_MAX', isset($config_urls_propres['URLS_PROPRES_MAX'])?$config_urls_propres['URLS_PROPRES_MAX']:80);
  44. if (!defined('_URLS_PROPRES_MIN')) define('_URLS_PROPRES_MIN', isset($config_urls_propres['URLS_PROPRES_MIN'])?$config_urls_propres['URLS_PROPRES_MIN']:3);
  45. if (!defined('_url_sep_id')) define('_url_sep_id',_url_propres_sep_id);
  46. // Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
  47. // indiquer la table ou les chercher (articles, auteurs etc),
  48. // et elles etaient retirees par les preg_match dans la fonction ci-dessous.
  49. // Elles peuvent a present etre definies a "" pour avoir des URL plus jolies.
  50. // Les preg_match restent necessaires pour gerer les anciens signets.
  51. if (!defined('_MARQUEUR_URL')) define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
  52. // Retire les marqueurs de type dans une URL propre ancienne maniere
  53. // http://code.spip.net/@retirer_marqueurs_url_propre
  54. function retirer_marqueurs_url_propre($url_propre) {
  55. if (preg_match(',^[+][-](.*?)[-][+]$,', $url_propre, $regs)) {
  56. return $regs[1];
  57. }
  58. else if (preg_match(',^([-+_@])(.*?)\1?$,', $url_propre, $regs)) {
  59. return $regs[2];
  60. }
  61. // les articles n'ont pas de marqueur
  62. return $url_propre;
  63. }
  64. // Pipeline pour creation d'une adresse : il recoit l'url propose par le
  65. // precedent, un tableau indiquant le titre de l'objet, son type, son id,
  66. // et doit donner en retour une chaine d'url, sans se soucier de la
  67. // duplication eventuelle, qui sera geree apres
  68. // http://code.spip.net/@creer_chaine_url
  69. function urls_propres_creer_chaine_url($x) {
  70. // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
  71. // pourrait l'utiliser pour juste ajouter la
  72. $url_old = $x['data'];
  73. $objet = $x['objet'];
  74. include_spip('inc/filtres');
  75. include_spip('action/editer_url');
  76. if (!$url = url_nettoyer($objet['titre'],_URLS_PROPRES_MAX,_URLS_PROPRES_MIN,'-',_url_minuscules?'strtolower':''))
  77. $url = $objet['type'].$objet['id_objet'];
  78. $x['data'] = $url;
  79. return $x;
  80. }
  81. // Trouver l'URL associee a la n-ieme cle primaire d'une table SQL
  82. // http://code.spip.net/@declarer_url_propre
  83. function declarer_url_propre($type, $id_objet) {
  84. $trouver_table = charger_fonction('trouver_table', 'base');
  85. $desc = $trouver_table(table_objet($type));
  86. $table = $desc['table'];
  87. $champ_titre = $desc['titre'] ? $desc['titre'] : 'titre';
  88. $col_id = @$desc['key']["PRIMARY KEY"];
  89. if (!$col_id) return false; // Quand $type ne reference pas une table
  90. $id_objet = intval($id_objet);
  91. // Recuperer une URL propre correspondant a l'objet.
  92. // mais urls a 1 segment uniquement (pas d'urls /)
  93. // de preference avec id_parent=0, puis perma, puis par date desc
  94. $row = sql_fetsel("U.url, U.date, U.id_parent, U.perma, $champ_titre",
  95. "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)",
  96. "O.$col_id=$id_objet AND (U.segments IS NULL OR U.segments=1)", '', 'U.id_parent=0 DESC, U.perma DESC, U.date DESC', 1);
  97. // en SQLite le left join retourne du vide si il y a une url mais qui ne correspond pas pour la condition sur le segment
  98. // on verifie donc que l'objet existe bien avant de sortir ou de creer une url pour cet objet
  99. if (!$row)
  100. $row = sql_fetsel("'' as url, '' as date, 0 as id_parent, 0 as perma, $champ_titre",
  101. "$table AS O",
  102. "O.$col_id=$id_objet");
  103. if (!$row) return ""; # Quand $id_objet n'est pas un numero connu
  104. $url_propre = $row['url'];
  105. // si url_propre connue mais avec id_parent non nul, essayer de reinserer tel quel avec id_parent=0
  106. if ($url_propre AND $row['id_parent']){
  107. include_spip('action/editer_url');
  108. $set = array('url' => $url_propre, 'type' => $type, 'id_objet' => $id_objet, 'perma' => $row['perma']);
  109. // si on arrive pas a reinserer tel quel, on annule url_propre pour forcer un recalcul d'url
  110. if (!url_insert($set,false,_url_propres_sep_id))
  111. $url_propre = "";
  112. else
  113. $url_propre = $row['url'] = $set['url'];
  114. }
  115. // Se contenter de cette URL si elle existe ;
  116. // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
  117. // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
  118. // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
  119. // qui requetent en base
  120. $modifier_url = (defined('_VAR_URLS') AND _VAR_URLS AND !$row['perma']);
  121. if ($url_propre AND !$modifier_url)
  122. return $url_propre;
  123. // Sinon, creer une URL
  124. $url = pipeline('propres_creer_chaine_url',
  125. array(
  126. 'data' => $url_propre, // le vieux url_propre
  127. 'objet' => array_merge($row,
  128. array('type' => $type, 'id_objet' => $id_objet)
  129. )
  130. )
  131. );
  132. // Eviter de tamponner les URLs a l'ancienne (cas d'un article
  133. // intitule "auteur2")
  134. include_spip('inc/urls');
  135. $objets = urls_liste_objets();
  136. if (preg_match(',^('.$objets.')[0-9]+$,', $url, $r)
  137. AND $r[1] != $type)
  138. $url = $url._url_propres_sep_id.$id_objet;
  139. // Pas de changement d'url
  140. if ($url == $url_propre)
  141. return $url_propre;
  142. // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
  143. if ($modifier_url) {
  144. include_spip('inc/autoriser');
  145. $modifier_url = autoriser('modifierurl', $type, $id_objet);
  146. }
  147. // Verifier si l'utilisateur veut effectivement changer l'URL
  148. if ($modifier_url
  149. AND CONFIRMER_MODIFIER_URL
  150. AND $url_propre
  151. AND $url != preg_replace('/'.preg_quote(_url_propres_sep_id,'/').'.*/', '', $url_propre))
  152. $confirmer = true;
  153. else
  154. $confirmer = false;
  155. if ($confirmer AND !_request('ok')) {
  156. die ("vous changez d'url ? $url_propre -&gt; $url");
  157. }
  158. $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet);
  159. include_spip('action/editer_url');
  160. if (!url_insert($set,$confirmer,_url_propres_sep_id))
  161. return $url_propre; //serveur out ? retourner au mieux
  162. return $set['url'];
  163. }
  164. // http://code.spip.net/@_generer_url_propre
  165. function _generer_url_propre($type, $id, $args='', $ancre='') {
  166. if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
  167. $url = $generer_url_externe($id, $args, $ancre);
  168. if (NULL != $url) return $url;
  169. }
  170. // Mode compatibilite pour conserver la distinction -Rubrique-
  171. if (_MARQUEUR_URL) {
  172. $marqueur = unserialize(_MARQUEUR_URL);
  173. $marqueur1 = isset($marqueur[$type.'1']) ? $marqueur[$type.'1'] : '' ; // debut '+-'
  174. $marqueur2 = isset($marqueur[$type.'2']) ? $marqueur[$type.'2'] : '' ; // fin '-+'
  175. } else
  176. $marqueur1 = $marqueur2 = '';
  177. // fin
  178. // Mode propre
  179. $propre = declarer_url_propre($type, $id);
  180. if ($propre === false) return ''; // objet inconnu. raccourci ?
  181. if ($propre) {
  182. $url = _debut_urls_propres
  183. . $marqueur1
  184. . $propre
  185. . $marqueur2
  186. . _terminaison_urls_propres;
  187. if (!defined('_SET_HTML_BASE') OR !_SET_HTML_BASE)
  188. // Repositionne l'URL par rapport a la racine du site (#GLOBALS)
  189. $url = str_repeat('../', $GLOBALS['profondeur_url']).$url;
  190. else
  191. $url = _DIR_RACINE . $url;
  192. } else {
  193. // objet connu mais sans possibilite d'URL lisible, revenir au defaut
  194. include_spip('base/connect_sql');
  195. $id_type = id_table_objet($type);
  196. $url = _DIR_RACINE . get_spip_script('./')."?"._SPIP_PAGE."=$type&$id_type=$id";
  197. }
  198. // Ajouter les args
  199. if ($args)
  200. $url .= ((strpos($url, '?')===false) ? '?' : '&') . $args;
  201. // Ajouter l'ancre
  202. if ($ancre)
  203. $url .= "#$ancre";
  204. return $url;
  205. }
  206. // retrouve le fond et les parametres d'une URL propre
  207. // ou produit une URL propre si on donne un parametre
  208. // @return array([contexte],[type],[url_redirect],[fond]) : url decodee
  209. // http://code.spip.net/@urls_propres_dist
  210. function urls_propres_dist($i, $entite, $args='', $ancre='') {
  211. if (is_numeric($i))
  212. return _generer_url_propre($entite, $i, $args, $ancre);
  213. $url = $i;
  214. $id_objet = $type = 0;
  215. $url_redirect = null;
  216. // recuperer les &debut_xx;
  217. if (is_array($args))
  218. $contexte = $args;
  219. else
  220. parse_str($args,$contexte);
  221. // Migration depuis anciennes URLs ?
  222. // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
  223. if ($GLOBALS['profondeur_url']<=0
  224. AND $_SERVER['REQUEST_METHOD'] != 'POST') {
  225. include_spip('inc/urls');
  226. $r = nettoyer_url_page($i, $contexte);
  227. if ($r) {
  228. list($contexte, $type,,, $suite) = $r;
  229. $_id = id_table_objet($type);
  230. $id_objet = $contexte[$_id];
  231. $url_propre = generer_url_entite($id_objet, $type);
  232. if (strlen($url_propre)
  233. AND !strstr($url,$url_propre)) {
  234. list(,$hash) = explode('#', $url_propre);
  235. $args = array();
  236. foreach(array_filter(explode('&', $suite)) as $fragment) {
  237. if ($fragment != "$_id=$id_objet")
  238. $args[] = $fragment;
  239. }
  240. $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
  241. return array($contexte, $type, $url_redirect, $type);
  242. }
  243. }
  244. }
  245. /* Fin compatibilite anciennes urls */
  246. // Chercher les valeurs d'environnement qui indiquent l'url-propre
  247. if (isset($_SERVER['REDIRECT_url_propre']))
  248. $url_propre = $_SERVER['REDIRECT_url_propre'];
  249. elseif (isset($_ENV['url_propre']))
  250. $url_propre = $_ENV['url_propre'];
  251. else {
  252. // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
  253. $url = ltrim($url,'/');
  254. $url = explode('/',$url);
  255. while (count($url)>$GLOBALS['profondeur_url']+1)
  256. array_shift($url);
  257. $url = implode('/',$url);
  258. $url_propre = preg_replace(',[?].*,', '', $url);
  259. }
  260. // Mode Query-String ?
  261. $is_qs = false;
  262. if (!$url_propre
  263. AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
  264. $url_propre = $r[1];
  265. $is_qs = true;
  266. }
  267. if (!$url_propre
  268. OR $url_propre==_DIR_RESTREINT_ABS
  269. OR $url_propre==_SPIP_SCRIPT) return; // qu'est-ce qu'il veut ???
  270. // gerer le cas de retour depuis des urls arbos
  271. // mais si url arbo ne trouve pas, on veut une 404 par securite
  272. if ($GLOBALS['profondeur_url']>0 AND !defined('_FORCE_URLS_PROPRES')){
  273. $urls_anciennes = charger_fonction('arbo','urls');
  274. return $urls_anciennes($url_propre, $entite, $contexte);
  275. }
  276. include_spip('base/abstract_sql'); // chercher dans la table des URLS
  277. // Compatibilite avec propres2
  278. $url_propre = preg_replace(',\.html$,i', '', $url_propre);
  279. // Revenir en utf-8 si encodage type %D8%A7 (farsi)
  280. $url_propre = rawurldecode($url_propre);
  281. // Compatibilite avec les anciens marqueurs d'URL propres
  282. // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls)
  283. if (!$row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre, '', 'TEXT'))) {
  284. // Sinon enlever les marqueurs eventuels
  285. $url_propre2 = retirer_marqueurs_url_propre($url_propre);
  286. $row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre2, '', 'TEXT'));
  287. }
  288. if ($row) {
  289. $type = $row['type'];
  290. $col_id = id_table_objet($type);
  291. $contexte[$col_id] = $row['id_objet'];
  292. $entite = $row['type'];
  293. // Si l'url est vieux, donner le nouveau
  294. if ($recent = sql_fetsel('url, date', 'spip_urls',
  295. 'type='.sql_quote($row['type'], '', 'TEXT').' AND id_objet='.sql_quote($row['id_objet'])
  296. .' AND date>'.sql_quote($row['date'], '', 'TEXT')
  297. .' AND url<>'.sql_quote($row['url'], '', 'TEXT'), '', 'date DESC', 1)) {
  298. // Mode compatibilite pour conserver la distinction -Rubrique-
  299. if (_MARQUEUR_URL) {
  300. $marqueur = unserialize(_MARQUEUR_URL);
  301. $marqueur1 = $marqueur[$type.'1']; // debut '+-'
  302. $marqueur2 = $marqueur[$type.'2']; // fin '-+'
  303. } else
  304. $marqueur1 = $marqueur2 = '';
  305. $url_redirect = $marqueur1 . $recent['url'] . $marqueur2;
  306. }
  307. }
  308. if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
  309. if ($type) {
  310. $entite = objet_type($type);
  311. } else {
  312. // Si ca ressemble a une URL d'objet, ce n'est pas la home
  313. // et on provoque un 404
  314. if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
  315. $entite = '404';
  316. $contexte['erreur'] = '';
  317. // l'url n'existe pas...
  318. // on ne sait plus dire de quel type d'objet il s'agit
  319. // sauf si on a le marqueur. et la c'est un peu sale...
  320. if (_MARQUEUR_URL) {
  321. $fmarqueur = @array_flip(unserialize(_MARQUEUR_URL));
  322. preg_match(',^([+][-]|[-+@_]),', $url_propre, $regs);
  323. $objet = $regs ? substr($fmarqueur[$regs[1]],0,n-1) : 'article';
  324. $contexte['erreur'] = _T(
  325. ($objet=='rubrique' OR $objet=='breve')
  326. ? 'public:aucune_'.$objet
  327. : 'public:aucun_'.$objet
  328. );
  329. }
  330. }
  331. }
  332. }
  333. return array($contexte, $entite, $url_redirect, $is_qs?$entite:null);
  334. }
  335. ?>