[SPIP] +2.1.12
[velocampus/web/www.git] / www / ecrire / urls / arbo.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2011 *
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;
14
15 define('URLS_ARBO_EXEMPLE', '/article/titre');
16
17 // TODO: une interface permettant de verifier qu'on veut effectivment modifier
18 // une adresse existante
19 define('CONFIRMER_MODIFIER_URL', false);
20
21 /**
22 * - Comment utiliser ce jeu d'URLs ?
23 * Recopiez le fichier "htaccess.txt" du repertoire de base du site SPIP sous
24 * le sous le nom ".htaccess" (attention a ne pas ecraser d'autres reglages
25 * que vous pourriez avoir mis dans ce fichier) ; si votre site est en
26 * "sous-repertoire", vous devrez aussi editer la ligne "RewriteBase" ce fichier.
27 * Les URLs definies seront alors redirigees vers les fichiers de SPIP.
28 *
29 * Choisissez "arbo" dans les pages de configuration d'URL
30 *
31 * SPIP calculera alors ses liens sous la forme "Mon-titre-d-article".
32 * Variantes :
33 *
34 * Terminaison :
35 * les terminaisons ne *sont pas* stockees en base, elles servent juste
36 * a rendre les url jolies ou conformes a un usage
37 * pour avoir des url terminant par html
38 * define ('_terminaison_urls_arbo', '.html');
39 *
40 * pour preciser des terminaisons particulieres pour certains types
41 * $GLOBALS['url_arbo_terminaisons']=array(
42 * 'rubrique' => '/',
43 * 'mot' => '',
44 * 'groupe' => '/',
45 * 'defaut' => '.html');
46 *
47 * pour avoir des url numeriques (id) du type 12/5/4/article/23
48 * define ('_URLS_ARBO_MIN',255);
49 *
50 *
51 * pour conserver la casse des titres dans les url
52 * define ('_url_arbo_minuscules',0);
53 *
54 * pour choisir le caractere de separation titre-id en cas de doublon
55 * (ne pas utiliser '/')
56 * define ('_url_arbo_sep_id','-');
57 *
58 * pour modifier la hierarchie apparente dans la constitution des urls
59 * ex pour que les mots soient classes par groupes
60 * $GLOBALS['url_arbo_parents']=array(
61 * 'article'=>array('id_rubrique','rubrique'),
62 * 'rubrique'=>array('id_parent','rubrique'),
63 * 'breve'=>array('id_rubrique','rubrique'),
64 * 'site'=>array('id_rubrique','rubrique'),
65 * 'mot'=>array('id_groupe','groupes_mot'));
66 *
67 * pour personaliser les types
68 * $GLOBALS['url_arbo_types']=array(
69 * 'rubrique'=>'', // pas de type pour les rubriques
70 * 'article'=>'a',
71 * 'mot'=>'tags'
72 * );
73 *
74 */
75
76
77 define ('_debut_urls_arbo', '');
78 define ('_terminaison_urls_arbo', '');
79 define ('_url_arbo_sep_id','-');
80 define ('_url_arbo_minuscules',1);
81
82 // Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
83 // indiquer la table ou les chercher (articles, auteurs etc),
84 // et elles etaient retirees par les preg_match dans la fonction ci-dessous.
85 // Elles sont a present definies a "" pour avoir des URL plus jolies
86 // mais les preg_match restent necessaires pour gerer les anciens signets.
87
88 #define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
89 define('_MARQUEUR_URL', false);
90
91 function url_arbo_parent($type){
92 static $parents = null;
93 if (is_null($parents)){
94 $parents = array(
95 'article'=>array('id_rubrique','rubrique'),
96 'rubrique'=>array('id_parent','rubrique'),
97 'breve'=>array('id_rubrique','rubrique'),
98 'site'=>array('id_rubrique','rubrique'));
99 if (isset($GLOBALS['url_arbo_parents']) AND !isset($_REQUEST['url_arbo_parents'])){
100 $parents = array_merge($parents,$GLOBALS['url_arbo_parents']);
101 }
102 }
103 return (isset($parents[$type])?$parents[$type]:'');
104 }
105
106 function url_arbo_terminaison($type){
107 static $terminaison_types = null;
108 if ($terminaison_types==null){
109 $terminaison_types = array('rubrique' => '/','mot' => '','defaut' => defined('_terminaison_urls_arbo')?_terminaison_urls_arbo:'.html');
110 if (isset($GLOBALS['url_arbo_terminaisons']))
111 $terminaison_types = array_merge($terminaison_types,$GLOBALS['url_arbo_terminaisons']);
112 }
113 // si c'est un appel avec type='' c'est pour avoir la liste des terminaisons
114 if (!$type)
115 return array_unique(array_values($terminaison_types));
116 if (isset($terminaison_types[$type]))
117 return $terminaison_types[$type];
118 elseif (isset($terminaison_types['defaut']))
119 return $terminaison_types['defaut'];
120 return "";
121 }
122
123 function url_arbo_type($type){
124 // par defaut les rubriques ne sont pas typees, mais le reste oui
125 static $synonymes_types = null;
126 if (!$synonymes_types){
127 $synonymes_types = array('rubrique'=>'');
128 if (isset($GLOBALS['url_arbo_types']) AND is_array($GLOBALS['url_arbo_types']))
129 $synonymes_types = array_merge($synonymes_types,$GLOBALS['url_arbo_types']);
130 }
131 // si c'est un appel avec type='' c'est pour avoir la liste inversee des synonymes
132 if (!$type)
133 return array_flip($synonymes_types);
134 return
135 ($t=(isset($synonymes_types[$type])?$synonymes_types[$type]:$type)) // le type ou son synonyme
136 . ($t?'/':''); // le / eventuel pour separer, si le synonyme n'est pas vide
137 }
138
139 // Pipeline pour creation d'une adresse : il recoit l'url propose par le
140 // precedent, un tableau indiquant le titre de l'objet, son type, son id,
141 // et doit donner en retour une chaine d'url, sans se soucier de la
142 // duplication eventuelle, qui sera geree apres
143 // http://doc.spip.org/@creer_chaine_url
144 function urls_arbo_creer_chaine_url($x) {
145 // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
146 // pourrait l'utiliser pour juste ajouter la
147 $url_old = $x['data'];
148 $objet = $x['objet'];
149 include_spip('inc/filtres');
150 if (!defined('_URLS_ARBO_MAX')) define('_URLS_ARBO_MAX', 35);
151 if (!defined('_URLS_ARBO_MIN')) define('_URLS_ARBO_MIN', 3);
152
153 include_spip('action/editer_url');
154 if (!$url = url_nettoyer($objet['titre'],_URLS_ARBO_MAX,_URLS_ARBO_MIN,'-',_url_arbo_minuscules?'strtolower':''))
155 $url = $objet['id_objet'];
156
157 $x['data'] =
158 url_arbo_type($objet['type']) // le type ou son synonyme
159 . $url; // le titre
160
161 return $x;
162 }
163
164 // http://doc.spip.org/@declarer_url_arbo_rec
165 function declarer_url_arbo_rec($url,$type,$parent,$type_parent){
166 if (is_null($parent)){
167 return $url;
168 }
169 if($parent==0)
170 return rtrim($url,'/');
171 else {
172 $url_parent = declarer_url_arbo($type_parent?$type_parent:'rubrique',$parent);
173 return rtrim($url_parent,'/') . '/' . rtrim($url,'/');
174 }
175 }
176
177 // http://doc.spip.org/@declarer_url_arbo
178 function declarer_url_arbo($type, $id_objet) {
179 static $urls=array();
180
181 // Se contenter de cette URL si elle existe ;
182 // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
183
184 // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
185 // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
186 // qui requetent en base
187 $modifier_url = $GLOBALS['var_urls'];
188
189 if (!isset($urls[$type][$id_objet]) OR $modifier_url) {
190 $trouver_table = charger_fonction('trouver_table', 'base');
191 $desc = $trouver_table(table_objet($type));
192 $champ_titre = $desc['titre'];
193 $col_id = @$desc['key']["PRIMARY KEY"];
194 // $type doit designer une table, avec champ indiquant un titre
195 if (!$col_id OR !$champ_titre) return false;
196
197 $table = $desc['table'];
198 $id_objet = intval($id_objet);
199
200
201 // parent
202 $champ_parent = url_arbo_parent($type);
203 $sel_parent = ($champ_parent)?", O.".reset($champ_parent).' as parent':'';
204
205 // Recuperer une URL propre correspondant a l'objet.
206 $row = sql_fetsel("U.url, U.date, O.$champ_titre $sel_parent", "$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);
207 if ($row){
208 $urls[$type][$id_objet] = $row;
209 $urls[$type][$id_objet]['type_parent'] = $champ_parent?end($champ_parent):'';
210 }
211 }
212
213 if (!isset($urls[$type][$id_objet])) return ""; # objet inexistant
214
215 $url_propre = $urls[$type][$id_objet]['url'];
216
217 if (!is_null($url_propre) AND !$modifier_url)
218 return declarer_url_arbo_rec($url_propre,$type,
219 isset($urls[$type][$id_objet]['parent'])?$urls[$type][$id_objet]['parent']:null,
220 isset($urls[$type][$id_objet]['type_parent'])?$urls[$type][$id_objet]['type_parent']:null);
221
222 // Sinon, creer une URL
223 $url = pipeline('arbo_creer_chaine_url',
224 array(
225 'data' => $url_propre, // le vieux url_propre
226 'objet' => array_merge($urls[$type][$id_objet],
227 array('type' => $type, 'id_objet' => $id_objet)
228 )
229 )
230 );
231
232 // Eviter de tamponner les URLs a l'ancienne (cas d'un article
233 // intitule "auteur2")
234 include_spip('inc/urls');
235 $objets = urls_liste_objets();
236 if (preg_match(',^('.$objets.')[0-9]*$,', $url, $r)
237 AND $r[1] != $type)
238 $url = $url._url_arbo_sep_id.$id_objet;
239
240 // Pas de changement d'url
241 if ($url == $url_propre)
242 return declarer_url_arbo_rec($url_propre,$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
243
244 // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
245 if ($modifier_url) {
246 include_spip('inc/autoriser');
247 $modifier_url = autoriser('modifierurl', $type, $id_objet);
248 }
249 // Verifier si l'utilisateur veut effectivement changer l'URL
250 if ($modifier_url
251 AND CONFIRMER_MODIFIER_URL
252 AND $url_propre
253 AND $url != preg_replace('/,.*/', '', $url_propre))
254 $confirmer = true;
255 else
256 $confirmer = false;
257
258 if ($confirmer AND !_request('ok')) {
259 die ("vous changez d'url ? $url_propre -&gt; $url");
260 }
261
262 $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet);
263 include_spip('action/editer_url');
264 if (url_insert($set,$confirmer,_url_arbo_sep_id)){
265 $urls[$type][$id_objet]['url'] = $set['url'];
266 }
267 else {
268 // l'insertion a echoue,
269 //serveur out ? retourner au mieux
270 $urls[$type][$id_objet]['url']=$url_propre;
271 }
272
273 return declarer_url_arbo_rec($urls[$type][$id_objet]['url'],$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
274 }
275
276 // http://doc.spip.org/@_generer_url_arbo
277 function _generer_url_arbo($type, $id, $args='', $ancre='') {
278
279 if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
280 $url = $generer_url_externe($id, $args, $ancre);
281 if (NULL != $url) return $url;
282 }
283
284 if ($type == 'document') {
285 include_spip('inc/documents');
286 return generer_url_document_dist($id, $args, $ancre);
287 }
288
289 // Mode propre
290 $propre = declarer_url_arbo($type, $id);
291
292 if ($propre === false) return ''; // objet inconnu. raccourci ?
293
294 if ($propre) {
295 $url = _debut_urls_arbo
296 . rtrim($propre,'/')
297 . url_arbo_terminaison($type);
298 } else {
299
300 // objet connu mais sans possibilite d'URL lisible, revenir au defaut
301 include_spip('base/connect_sql');
302 $id_type = id_table_objet($type);
303 $url = get_spip_script('./')."?"._SPIP_PAGE."=$type&$id_type=$id";
304 }
305
306 // Ajouter les args
307 if ($args)
308 $url .= ((strpos($url, '?')===false) ? '?' : '&') . $args;
309
310 // Ajouter l'ancre
311 if ($ancre)
312 $url .= "#$ancre";
313
314 return _DIR_RACINE . $url;
315 }
316
317
318 // @return array([contexte],[type],[url_redirect],[fond]) : url decodee
319 // http://doc.spip.org/@urls_arbo_dist
320 function urls_arbo_dist($i, $entite, $args='', $ancre='') {
321 if (is_numeric($i))
322 return _generer_url_arbo($entite, $i, $args, $ancre);
323
324 // traiter les injections du type domaine.org/spip.php/cestnimportequoi/ou/encore/plus/rubrique23
325 if ($GLOBALS['profondeur_url']>0 AND $entite=='sommaire'){
326 $entite = 'type_urls';
327 }
328
329 // recuperer les &debut_xx;
330 if (is_array($args))
331 $contexte = $args;
332 else
333 parse_str($args,$contexte);
334
335 $url = $i;
336 $id_objet = $type = 0;
337 $url_redirect = null;
338
339 // Migration depuis anciennes URLs ?
340 // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
341 if ($GLOBALS['profondeur_url']<=0
342 AND $_SERVER['REQUEST_METHOD'] != 'POST') {
343 include_spip('inc/urls');
344 $r = nettoyer_url_page($i, $contexte);
345 if ($r) {
346 list($contexte, $type,,, $suite) = $r;
347 $_id = id_table_objet($type);
348 $id_objet = $contexte[$_id];
349 $url_propre = generer_url_entite($id_objet, $type);
350 if (strlen($url_propre)
351 AND !strstr($url,$url_propre)) {
352 list(,$hash) = explode('#', $url_propre);
353 $args = array();
354 foreach(array_filter(explode('&', $suite)) as $fragment) {
355 if ($fragment != "$_id=$id_objet")
356 $args[] = $fragment;
357 }
358 $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
359
360 return array($contexte, $type, $url_redirect, $type);
361 }
362 }
363 }
364 /* Fin compatibilite anciennes urls */
365
366 // Chercher les valeurs d'environnement qui indiquent l'url-propre
367 if (isset($_SERVER['REDIRECT_url_propre']))
368 $url_propre = $_SERVER['REDIRECT_url_propre'];
369 elseif (isset($_ENV['url_propre']))
370 $url_propre = $_ENV['url_propre'];
371 else {
372 // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
373 $url = ltrim($url,'/');
374 $url = explode('/',$url);
375 while (count($url)>$GLOBALS['profondeur_url']+1)
376 array_shift($url);
377 $url = implode('/',$url);
378 $url_propre = preg_replace(',[?].*,', '', $url);
379 }
380
381 // Mode Query-String ?
382 if (!$url_propre
383 AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
384 $url_propre = $r[1];
385 }
386
387 if (!$url_propre) return; // qu'est-ce qu'il veut ???
388
389 include_spip('base/abstract_sql'); // chercher dans la table des URLS
390
391 // Revenir en utf-8 si encodage type %D8%A7 (farsi)
392 $url_propre = rawurldecode($url_propre);
393
394 // Compatibilite avec .htm/.html et autres terminaisons
395 $t = array_diff(array_unique(array_merge(array('.html','.htm','/'),url_arbo_terminaison(''))),array(''));
396 if (count($t))
397 $url_propre = preg_replace('{('
398 .implode('|',array_map('preg_quote',$t)).')$}i', '', $url_propre);
399
400 if (strlen($url_propre) AND !preg_match(',^[^/]*[.]php,',$url_propre)){
401 $types_parents = array();
402
403 // recuperer tous les objets de larbo xxx/article/yyy/mot/zzzz
404 $url_arbo = explode('/',$url_propre);
405 while (count($url_arbo)>0){
406 $url_propre = array_pop($url_arbo);
407 if (count($url_arbo))
408 $type = array_pop($url_arbo);
409 else
410 $type=null;
411 // Compatibilite avec les anciens marqueurs d'URL propres
412 // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls)
413 if (is_null($type)
414 OR !$row=sql_fetsel('id_objet, type, date', 'spip_urls',array('url='.sql_quote("$type/$url_propre")))) {
415 if (!is_null($type))
416 array_push($url_arbo,$type);
417 $row = sql_fetsel('id_objet, type, date', 'spip_urls',array('url='.sql_quote($url_propre)));
418 }
419 if ($row) {
420 $type = $row['type'];
421 $col_id = id_table_objet($type);
422
423 // n'affecter que la premiere fois un parent de type id_rubrique
424 if (!isset($contexte[$col_id]))
425 $contexte[$col_id] = $row['id_objet'];
426
427 if (!$entite
428 OR !in_array($type,$types_parents))
429 $entite = $type;
430
431 if ($p = url_arbo_parent($type))
432 $types_parents[]=end($p);
433 }
434 else {
435 // un segment est inconnu
436 if ($entite=='' OR $entite=='type_urls') {
437 // on genere une 404 comme il faut si on ne sait pas ou aller
438 return array(array(),'404');
439 }
440 return; // ?
441 }
442 }
443
444 // gerer le retour depuis des urls propres
445 if (($entite=='' OR $entite=='type_urls')
446 AND $GLOBALS['profondeur_url']<=0){
447 $urls_anciennes = charger_fonction('propres','urls');
448 return $urls_anciennes($url_propre, $entite, $contexte);
449 }
450 }
451 if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
452 if ($type)
453 $entite = ($type == 'syndic') ? 'site' : $type;
454 else {
455 // Si ca ressemble a une URL d'objet, ce n'est pas la home
456 // et on provoque un 404
457 if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
458 $entite = '404';
459 $contexte['erreur'] = ''; // qu'afficher ici ? l'url n'existe pas... on ne sait plus dire de quel type d'objet il s'agit
460 }
461 }
462 }
463 define('_SET_HTML_BASE',1);
464
465 return array($contexte, $entite, null, $is_qs?$entite:null);
466 }
467
468 ?>