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