3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2011 *
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')) return;
15 define('URLS_PROPRES_EXEMPLE', 'Titre-de-l-article -Rubrique-');
17 // TODO: une interface permettant de verifier qu'on veut effectivment modifier
18 // une adresse existante
19 define('CONFIRMER_MODIFIER_URL', false);
23 - Comment utiliser ce jeu d'URLs ?
25 Recopiez le fichier "htaccess.txt" du repertoire de base du site SPIP sous
26 le sous le nom ".htaccess" (attention a ne pas ecraser d'autres reglages
27 que vous pourriez avoir mis dans ce fichier) ; si votre site est en
28 "sous-repertoire", vous devrez aussi editer la ligne "RewriteBase" ce fichier.
29 Les URLs definies seront alors redirigees vers les fichiers de SPIP.
31 Dans les pages de configuration, choisissez 'propres' comme type d'url
33 SPIP calculera alors ses liens sous la forme
34 "Mon-titre-d-article".
36 La variante 'propres2' ajoutera '.html' aux adresses generees :
37 "Mon-titre-d-article.html"
39 Variante 'qs' (experimentale) : ce systeme fonctionne en "Query-String",
40 c'est-a-dire sans utilisation de .htaccess ; les adresses sont de la forme
41 "/?Mon-titre-d-article"
44 define ('_terminaison_urls_propres', '');
45 define ('_debut_urls_propres', '');
47 // option pour tout passer en minuscules
48 define ('_url_minuscules',0);
50 // pour choisir le caractere de separation titre-id en cas de doublon
51 // (ne pas utiliser '/')
52 define ('_url_propres_sep_id',',');
54 // Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
55 // indiquer la table ou les chercher (articles, auteurs etc),
56 // et elles etaient retirees par les preg_match dans la fonction ci-dessous.
57 // Elles peuvent a present etre definies a "" pour avoir des URL plus jolies.
58 // Les preg_match restent necessaires pour gerer les anciens signets.
60 define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
62 // Retire les marqueurs de type dans une URL propre ancienne maniere
64 // http://doc.spip.org/@retirer_marqueurs_url_propre
65 function retirer_marqueurs_url_propre($url_propre) {
66 if (preg_match(',^[+][-](.*?)[-][+]$,', $url_propre, $regs)) {
69 else if (preg_match(',^([-+_@])(.*?)\1?$,', $url_propre, $regs)) {
72 // les articles n'ont pas de marqueur
77 // Pipeline pour creation d'une adresse : il recoit l'url propose par le
78 // precedent, un tableau indiquant le titre de l'objet, son type, son id,
79 // et doit donner en retour une chaine d'url, sans se soucier de la
80 // duplication eventuelle, qui sera geree apres
81 // http://doc.spip.org/@creer_chaine_url
82 function urls_propres_creer_chaine_url($x) {
83 // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
84 // pourrait l'utiliser pour juste ajouter la
85 $url_old = $x['data'];
87 include_spip('inc/filtres');
88 if (!defined('_URLS_PROPRES_MAX')) define('_URLS_PROPRES_MAX', 35);
89 if (!defined('_URLS_PROPRES_MIN')) define('_URLS_PROPRES_MIN', 3);
91 include_spip('action/editer_url');
92 if (!$url = url_nettoyer($objet['titre'],_URLS_PROPRES_MAX
,_URLS_PROPRES_MIN
,'-',_url_minuscules?
'strtolower':''))
93 $url = $objet['type'].$objet['id_objet'];
100 // Trouver l'URL associee a la n-ieme cle primaire d'une table SQL
102 // http://doc.spip.org/@declarer_url_propre
103 function declarer_url_propre($type, $id_objet) {
104 $trouver_table = charger_fonction('trouver_table', 'base');
105 $desc = $trouver_table(table_objet($type));
106 $champ_titre = $desc['titre'];
107 $col_id = @$desc['key']["PRIMARY KEY"];
108 // $type doit designer une table, avec champ indiquant un titre
109 if (!$col_id OR !$champ_titre) return false;
111 $table = $desc['table'];
112 $id_objet = intval($id_objet);
114 // Recuperer une URL propre correspondant a l'objet.
115 $row = sql_fetsel("U.url, U.date, O.$champ_titre", "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)", "O.$col_id=$id_objet", '', 'U.date DESC', 1);
117 if (!$row) return ""; # Quand $id_objet n'est pas un numero connu
119 $url_propre = $row['url'];
121 // Se contenter de cette URL si elle existe ;
122 // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
124 // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
125 // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
126 // qui requetent en base
127 $modifier_url = $GLOBALS['var_urls'];
128 if ($url_propre AND !$modifier_url)
131 // Sinon, creer une URL
132 $url = pipeline('propres_creer_chaine_url',
134 'data' => $url_propre, // le vieux url_propre
135 'objet' => array_merge($row,
136 array('type' => $type, 'id_objet' => $id_objet)
141 // Eviter de tamponner les URLs a l'ancienne (cas d'un article
142 // intitule "auteur2")
143 include_spip('inc/urls');
144 $objets = urls_liste_objets();
145 if (preg_match(',^('.$objets.')[0-9]+$,', $url, $r)
147 $url = $url._url_propres_sep_id
.$id_objet;
149 // Pas de changement d'url
150 if ($url == $url_propre)
153 // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
155 include_spip('inc/autoriser');
156 $modifier_url = autoriser('modifierurl', $type, $id_objet);
159 // Verifier si l'utilisateur veut effectivement changer l'URL
161 AND CONFIRMER_MODIFIER_URL
163 AND $url != preg_replace('/,.*/', '', $url_propre))
168 if ($confirmer AND !_request('ok')) {
169 die ("vous changez d'url ? $url_propre -> $url");
172 $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet);
173 include_spip('action/editer_url');
174 if (!url_insert($set,$confirmer,_url_propres_sep_id
))
175 return $url_propre; //serveur out ? retourner au mieux
180 // http://doc.spip.org/@_generer_url_propre
181 function _generer_url_propre($type, $id, $args='', $ancre='') {
182 if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
183 $url = $generer_url_externe($id, $args, $ancre);
184 if (NULL != $url) return $url;
187 if ($type == 'document') {
188 include_spip('inc/documents');
189 return generer_url_document_dist($id, $args, $ancre);
192 // Mode compatibilite pour conserver la distinction -Rubrique-
194 $marqueur = unserialize(_MARQUEUR_URL
);
195 $marqueur1 = $marqueur[$type.'1']; // debut '+-'
196 $marqueur2 = $marqueur[$type.'2']; // fin '-+'
198 $marqueur1 = $marqueur2 = '';
202 $propre = declarer_url_propre($type, $id);
204 if ($propre === false) return ''; // objet inconnu. raccourci ?
207 $url = _debut_urls_propres
211 . _terminaison_urls_propres
;
213 // Repositionne l'URL par rapport a la racine du site (#GLOBALS)
214 $url = str_repeat('../', $GLOBALS['profondeur_url']).$url;
217 // objet connu mais sans possibilite d'URL lisible, revenir au defaut
218 include_spip('base/connect_sql');
219 $id_type = id_table_objet($type);
220 $url = _DIR_RACINE
. get_spip_script('./')."?"._SPIP_PAGE
."=$type&$id_type=$id";
225 $url .= ((strpos($url, '?')===false) ?
'?' : '&') . $args;
234 // retrouve le fond et les parametres d'une URL propre
235 // ou produit une URL propre si on donne un parametre
236 // @return array([contexte],[type],[url_redirect],[fond]) : url decodee
237 // http://doc.spip.org/@urls_propres_dist
238 function urls_propres_dist($i, $entite, $args='', $ancre='') {
241 return _generer_url_propre($entite, $i, $args, $ancre);
244 $id_objet = $type = 0;
245 $url_redirect = null;
246 // recuperer les &debut_xx;
250 parse_str($args,$contexte);
253 // Migration depuis anciennes URLs ?
254 // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
255 if ($GLOBALS['profondeur_url']<=0
256 AND $_SERVER['REQUEST_METHOD'] != 'POST') {
257 include_spip('inc/urls');
258 $r = nettoyer_url_page($i, $contexte);
260 list($contexte, $type,,, $suite) = $r;
261 $_id = id_table_objet($type);
262 $id_objet = $contexte[$_id];
263 $url_propre = generer_url_entite($id_objet, $type);
264 if (strlen($url_propre)
265 AND !strstr($url,$url_propre)) {
266 list(,$hash) = explode('#', $url_propre);
268 foreach(array_filter(explode('&', $suite)) as $fragment) {
269 if ($fragment != "$_id=$id_objet")
272 $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
274 return array($contexte, $type, $url_redirect, $type);
278 /* Fin compatibilite anciennes urls */
279 // Chercher les valeurs d'environnement qui indiquent l'url-propre
280 if (isset($_SERVER['REDIRECT_url_propre']))
281 $url_propre = $_SERVER['REDIRECT_url_propre'];
282 elseif (isset($_ENV['url_propre']))
283 $url_propre = $_ENV['url_propre'];
285 // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
286 $url = ltrim($url,'/');
287 $url = explode('/',$url);
288 while (count($url)>$GLOBALS['profondeur_url']+
1)
290 $url = implode('/',$url);
291 $url_propre = preg_replace(',[?].*,', '', $url);
294 // Mode Query-String ?
297 AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
302 if (!$url_propre) return; // qu'est-ce qu'il veut ???
304 // gerer le cas de retour depuis des urls arbos
305 // mais si url arbo ne trouve pas, on veut une 404 par securite
306 if ($GLOBALS['profondeur_url']>0){
307 $urls_anciennes = charger_fonction('arbo','urls');
308 return $urls_anciennes($url_propre, $entite, $contexte);
311 include_spip('base/abstract_sql'); // chercher dans la table des URLS
313 // Compatibilite avec propres2
314 $url_propre = preg_replace(',\.html$,i', '', $url_propre);
316 // Revenir en utf-8 si encodage type %D8%A7 (farsi)
317 $url_propre = rawurldecode($url_propre);
319 // Compatibilite avec les anciens marqueurs d'URL propres
320 // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls)
321 if (!$row = sql_fetsel('id_objet, type, date', 'spip_urls', 'url='.sql_quote($url_propre))) {
322 // Sinon enlever les marqueurs eventuels
323 $url_propre2 = retirer_marqueurs_url_propre($url_propre);
325 $row = sql_fetsel('id_objet, type, date', 'spip_urls', 'url='.sql_quote($url_propre2));
329 $type = $row['type'];
330 $col_id = id_table_objet($type);
331 $contexte[$col_id] = $row['id_objet'];
332 $entite = $row['type'];
334 // Si l'url est vieux, donner le nouveau
335 if ($recent = sql_fetsel('url, date', 'spip_urls',
336 'type='.sql_quote($row['type']).' AND id_objet='.sql_quote($row['id_objet'])
337 .' AND date>'.sql_quote($row['date']), '', 'date DESC', 1)) {
338 // Mode compatibilite pour conserver la distinction -Rubrique-
340 $marqueur = unserialize(_MARQUEUR_URL
);
341 $marqueur1 = $marqueur[$type.'1']; // debut '+-'
342 $marqueur2 = $marqueur[$type.'2']; // fin '-+'
344 $marqueur1 = $marqueur2 = '';
345 $url_redirect = $marqueur1 . $recent['url'] . $marqueur2;
349 if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
351 $entite = ($type == 'syndic') ?
'site' : $type;
353 // Si ca ressemble a une URL d'objet, ce n'est pas la home
354 // et on provoque un 404
355 if (preg_match(',^.*/[^\.]+(\.html)?$,', $url)) {
357 $contexte['erreur'] = '';
359 // l'url n'existe pas...
360 // on ne sait plus dire de quel type d'objet il s'agit
361 // sauf si on a le marqueur. et la c'est un peu sale...
363 $fmarqueur = @array_flip
(unserialize(_MARQUEUR_URL
));
364 preg_match(',^([+][-]|[-+@_]),', $url_propre, $regs);
365 $objet = $regs ?
substr($fmarqueur[$regs[1]],0,n
-1) : 'article';
366 $contexte['erreur'] = _T(
367 ($objet=='rubrique' OR $objet=='breve')
368 ?
'public:aucune_'.$objet
369 : 'public:aucun_'.$objet
376 return array($contexte, $entite, $url_redirect, $is_qs?
$entite:null);