source: spip-zone/_core_/plugins/urls_etendues/urls/arbo.php @ 49318

Last change on this file since 49318 was 49318, checked in by cedric@…, 10 years ago

complement a r49317 : ne pas chercher a decoder l'url "spip.php" (form_hidden sur les urls action)

File size: 16.7 KB
Line 
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
13if (!defined("_ECRIRE_INC_VERSION")) return; // securiser
14
15define('URLS_ARBO_EXEMPLE', '/article/titre');
16
17// TODO: une interface permettant de verifier qu'on veut effectivment modifier
18// une adresse existante
19define('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
76include_spip('inc/xcache');
77if (!function_exists('Cache')) {
78        function Cache(){return null;}
79}
80
81
82define ('_debut_urls_arbo', '');
83define ('_terminaison_urls_arbo', '');
84define ('_url_arbo_sep_id','-');
85define ('_url_arbo_minuscules',1);
86
87// Ces chaines servaient de marqueurs a l'epoque ou les URL propres devaient
88// indiquer la table ou les chercher (articles, auteurs etc),
89// et elles etaient retirees par les preg_match dans la fonction ci-dessous.
90// Elles sont a present definies a "" pour avoir des URL plus jolies
91// mais les preg_match restent necessaires pour gerer les anciens signets.
92
93#define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
94define('_MARQUEUR_URL', false);
95
96function url_arbo_parent($type){
97        static $parents = null;
98        if (is_null($parents)){
99                $parents = array(
100                          'article'=>array('id_rubrique','rubrique'),
101                          'rubrique'=>array('id_parent','rubrique'),
102                          'breve'=>array('id_rubrique','rubrique'),
103                          'site'=>array('id_rubrique','rubrique'));
104                if (isset($GLOBALS['url_arbo_parents']) AND !isset($_REQUEST['url_arbo_parents'])){
105                        $parents = array_merge($parents,$GLOBALS['url_arbo_parents']);
106                }                         
107        }
108        return (isset($parents[$type])?$parents[$type]:'');
109}
110
111function url_arbo_terminaison($type){
112        static $terminaison_types = null;
113        if ($terminaison_types==null){
114                $terminaison_types = array('rubrique' => '/','mot' => '','defaut' => defined('_terminaison_urls_arbo')?_terminaison_urls_arbo:'.html');
115                if (isset($GLOBALS['url_arbo_terminaisons']))
116                        $terminaison_types = array_merge($terminaison_types,$GLOBALS['url_arbo_terminaisons']);
117        }
118        // si c'est un appel avec type='' c'est pour avoir la liste des terminaisons
119        if (!$type)
120                return array_unique(array_values($terminaison_types));
121        if (isset($terminaison_types[$type]))
122                return $terminaison_types[$type];
123        elseif (isset($terminaison_types['defaut']))
124                return $terminaison_types['defaut'];
125        return "";
126}
127
128function url_arbo_type($type){
129        // par defaut les rubriques ne sont pas typees, mais le reste oui
130        static $synonymes_types = null;
131        if (!$synonymes_types){
132                $synonymes_types = array('rubrique'=>'');
133                if (isset($GLOBALS['url_arbo_types']) AND is_array($GLOBALS['url_arbo_types']))
134                        $synonymes_types = array_merge($synonymes_types,$GLOBALS['url_arbo_types']);
135        }
136        // si c'est un appel avec type='' c'est pour avoir la liste inversee des synonymes
137        if (!$type)
138                return array_flip($synonymes_types);
139        return 
140            ($t=(isset($synonymes_types[$type])?$synonymes_types[$type]:$type))  // le type ou son synonyme
141          . ($t?'/':''); // le / eventuel pour separer, si le synonyme n'est pas vide
142}
143
144// Pipeline pour creation d'une adresse : il recoit l'url propose par le
145// precedent, un tableau indiquant le titre de l'objet, son type, son id,
146// et doit donner en retour une chaine d'url, sans se soucier de la
147// duplication eventuelle, qui sera geree apres
148// http://doc.spip.org/@creer_chaine_url
149function urls_arbo_creer_chaine_url($x) {
150        // NB: ici url_old ne sert pas, mais un plugin qui ajouterait une date
151        // pourrait l'utiliser pour juste ajouter la
152        $url_old = $x['data'];
153        $objet = $x['objet'];
154        include_spip('inc/filtres');
155        @define('_URLS_ARBO_MAX', 35);
156        @define('_URLS_ARBO_MIN', 3);
157
158        include_spip('action/editer_url');
159        if (!$url = url_nettoyer($objet['titre'],_URLS_ARBO_MAX,_URLS_ARBO_MIN,'-',_url_arbo_minuscules?'strtolower':''))
160                $url = $objet['id_objet'];
161       
162        $x['data'] = 
163                url_arbo_type($objet['type']) // le type ou son synonyme
164          . $url; // le titre
165
166        return $x;
167}
168
169// http://doc.spip.org/@declarer_url_arbo_rec
170function declarer_url_arbo_rec($url,$type,$parent,$type_parent){
171        if (is_null($parent)){
172                return $url;
173        }
174        if($parent==0)
175                return rtrim($url,'/');
176        else {
177                $url_parent = declarer_url_arbo($type_parent?$type_parent:'rubrique',$parent);
178                return rtrim($url_parent,'/') . '/' . rtrim($url,'/');
179        }
180}
181
182// http://doc.spip.org/@declarer_url_arbo
183function declarer_url_arbo($type, $id_objet) {
184        static $urls=array();
185        // utiliser un cache memoire pour aller plus vite
186        if(!is_null($C=Cache())) return$C;
187       
188        // Se contenter de cette URL si elle existe ;
189        // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
190
191        // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
192        // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
193        // qui requetent en base
194        $modifier_url = (defined('_VAR_URLS') AND _VAR_URLS);
195       
196        if (!isset($urls[$type][$id_objet]) OR $modifier_url) {
197                $trouver_table = charger_fonction('trouver_table', 'base');
198                $desc = $trouver_table(table_objet($type));
199                $table = $desc['table'];
200                $col_id =  @$desc['key']["PRIMARY KEY"];
201                if (!$col_id) return false; // Quand $type ne reference pas une table
202                $id_objet = intval($id_objet);
203       
204                // Auteurs : on prend le nom
205                if ($type == 'auteur')
206                        $champ_titre = 'nom AS titre';
207                else if ($type == 'site' OR $type=='syndic')
208                        $champ_titre = 'nom_site AS titre';
209                else
210                        $champ_titre = 'titre';
211                       
212                // parent
213                $champ_parent = url_arbo_parent($type);
214                $sel_parent = ($champ_parent)?", O.".reset($champ_parent).' as parent':'';
215       
216                //  Recuperer une URL propre correspondant a l'objet.
217                $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);
218                if ($row){
219                        $urls[$type][$id_objet] = $row;
220                        $urls[$type][$id_objet]['type_parent'] = $champ_parent?end($champ_parent):'';
221                }
222        }
223
224        if (!isset($urls[$type][$id_objet])) return ""; # objet inexistant
225
226        $url_propre = $urls[$type][$id_objet]['url'];
227
228        if (!is_null($url_propre) AND !$modifier_url)
229                return declarer_url_arbo_rec($url_propre,$type,
230                  isset($urls[$type][$id_objet]['parent'])?$urls[$type][$id_objet]['parent']:null,
231                  isset($urls[$type][$id_objet]['type_parent'])?$urls[$type][$id_objet]['type_parent']:null);
232
233        // Sinon, creer une URL
234        $url = pipeline('arbo_creer_chaine_url',
235                array(
236                        'data' => $url_propre,  // le vieux url_propre
237                        'objet' => array_merge($urls[$type][$id_objet],
238                                array('type' => $type, 'id_objet' => $id_objet)
239                        )
240                )
241        );
242
243        // Eviter de tamponner les URLs a l'ancienne (cas d'un article
244        // intitule "auteur2")
245        include_spip('inc/urls');
246        $objets = urls_liste_objets();
247        if (preg_match(',^('.$objets.')[0-9]*$,', $url, $r)
248        AND $r[1] != $type)
249                $url = $url._url_arbo_sep_id.$id_objet;
250
251        // Pas de changement d'url
252        if ($url == $url_propre)
253                return declarer_url_arbo_rec($url_propre,$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
254       
255        // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
256        if ($modifier_url) {
257                include_spip('inc/autoriser');
258                $modifier_url = autoriser('modifierurl', $type, $id_objet);
259        }
260        // Verifier si l'utilisateur veut effectivement changer l'URL
261        if ($modifier_url
262                AND CONFIRMER_MODIFIER_URL
263                AND $url_propre
264                AND $url != preg_replace('/,.*/', '', $url_propre))
265                $confirmer = true;
266        else
267                $confirmer = false;
268
269        if ($confirmer AND !_request('ok')) {
270                die ("vous changez d'url ? $url_propre -&gt; $url");
271        }
272
273        $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet);
274        include_spip('action/editer_url');
275        if (url_insert($set,$confirmer,_url_arbo_sep_id)){
276                $urls[$type][$id_objet]['url'] = $set['url'];
277        }
278        else {
279                // l'insertion a echoue,
280                //serveur out ? retourner au mieux
281                $urls[$type][$id_objet]['url']=$url_propre;
282        }
283
284        return declarer_url_arbo_rec($urls[$type][$id_objet]['url'],$type,$urls[$type][$id_objet]['parent'],$urls[$type][$id_objet]['type_parent']);
285}
286
287// http://doc.spip.org/@_generer_url_arbo
288function _generer_url_arbo($type, $id, $args='', $ancre='') {
289
290        if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
291                $url = $generer_url_externe($id, $args, $ancre);
292                if (NULL != $url) return $url;
293        }
294
295        if ($type == 'document') {
296                include_spip('inc/documents');
297                return generer_url_document_dist($id, $args, $ancre);
298        }
299
300        // Mode propre
301        $propre = declarer_url_arbo($type, $id);
302
303        if ($propre === false) return ''; // objet inconnu. raccourci ?
304
305        if ($propre) {
306                $url = _debut_urls_arbo
307                        . rtrim($propre,'/')
308                        . url_arbo_terminaison($type);
309        } else {
310
311                // objet connu mais sans possibilite d'URL lisible, revenir au defaut
312                include_spip('base/connect_sql');
313                $id_type = id_table_objet($type);
314                $url = get_spip_script('./')."?"._SPIP_PAGE."=$type&$id_type=$id";
315        }
316
317        // Ajouter les args
318        if ($args)
319                $url .= ((strpos($url, '?')===false) ? '?' : '&') . $args;
320
321        // Ajouter l'ancre
322        if ($ancre)
323                $url .= "#$ancre";
324
325        return _DIR_RACINE . $url;
326}
327
328
329// @return array([contexte],[type],[url_redirect],[fond]) : url decodee
330// http://doc.spip.org/@urls_arbo_dist
331function urls_arbo_dist($i, $entite, $args='', $ancre='') {
332        if (is_numeric($i))
333                return _generer_url_arbo($entite, $i, $args, $ancre);
334
335        // traiter les injections du type domaine.org/spip.php/cestnimportequoi/ou/encore/plus/rubrique23
336        if ($GLOBALS['profondeur_url']>0 AND $entite=='sommaire'){
337                $entite = 'type_urls';
338        }
339
340        // recuperer les &debut_xx;
341        if (is_array($args))
342                $contexte = $args;
343        else
344                parse_str($args,$contexte);
345
346        $url = $i;
347        $id_objet = $type = 0;
348        $url_redirect = null;
349
350        // Migration depuis anciennes URLs ?
351        // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
352        if ($GLOBALS['profondeur_url']<=0
353        AND $_SERVER['REQUEST_METHOD'] != 'POST') {
354                include_spip('inc/urls');
355                $r = nettoyer_url_page($i, $contexte);
356                if ($r) {
357                        list($contexte, $type,,, $suite) = $r;
358                        $_id = id_table_objet($type);
359                        $id_objet = $contexte[$_id];
360                        $url_propre = generer_url_entite($id_objet, $type);
361                        if (strlen($url_propre)
362                        AND !strstr($url,$url_propre)) {
363                                list(,$hash) = explode('#', $url_propre);
364                                $args = array();
365                                foreach(array_filter(explode('&', $suite)) as $fragment) {
366                                        if ($fragment != "$_id=$id_objet")
367                                                $args[] = $fragment;
368                                }
369                                $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
370
371                                return array($contexte, $type, $url_redirect, $type);
372                        }
373                }
374        }
375        /* Fin compatibilite anciennes urls */
376
377        // Chercher les valeurs d'environnement qui indiquent l'url-propre
378        if (isset($_SERVER['REDIRECT_url_propre']))
379                $url_propre = $_SERVER['REDIRECT_url_propre'];
380        elseif (isset($_ENV['url_propre']))
381                $url_propre = $_ENV['url_propre'];
382        else {
383                // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
384                $url = ltrim($url,'/');
385                $url = explode('/',$url);
386                while (count($url)>$GLOBALS['profondeur_url']+1)
387                        array_shift($url);
388                $url = implode('/',$url);
389                $url_propre = preg_replace(',[?].*,', '', $url);
390        }
391
392        // Mode Query-String ?
393        if (!$url_propre
394        AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
395                $url_propre = $r[1];
396        }
397
398        if (!$url_propre
399          OR $url_propre==_DIR_RESTREINT_ABS
400          OR $url_propre==_SPIP_SCRIPT) return; // qu'est-ce qu'il veut ???
401
402       
403        include_spip('base/abstract_sql'); // chercher dans la table des URLS
404
405        // Revenir en utf-8 si encodage type %D8%A7 (farsi)
406        $url_propre = rawurldecode($url_propre);
407
408        // Compatibilite avec .htm/.html et autres terminaisons
409        $t = array_diff(array_unique(array_merge(array('.html','.htm','/'),url_arbo_terminaison(''))),array(''));
410        if (count($t))
411                $url_propre = preg_replace('{('
412                  .implode('|',array_map('preg_quote',$t)).')$}i', '', $url_propre);
413
414        if (strlen($url_propre) AND !preg_match(',^[^/]*[.]php,',$url_propre)){
415                $parents_vus = array();
416               
417                // recuperer tous les objets de larbo xxx/article/yyy/mot/zzzz
418                // on parcourt les segments de gauche a droite
419                // pour pouvoir contextualiser un segment par son parent
420                $url_arbo = explode('/',$url_propre);
421                while (count($url_arbo)>0){
422                        $type=null;
423                        if (count($url_arbo)>1)
424                                $type = array_shift($url_arbo);
425                        $url_segment = array_shift($url_arbo);
426                        // Compatibilite avec les anciens marqueurs d'URL propres
427                        // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls)
428                        if (is_null($type)
429                        OR !$row=sql_fetsel('id_objet, type, date', 'spip_urls',array('url='.sql_quote("$type/$url_segment")))) {
430                                if (!is_null($type)){
431                                        array_unshift($url_arbo,$url_segment);
432                                        $url_segment = $type;
433                                        $type = null;
434                                }
435                                $row = sql_fetsel('id_objet, type, date', 'spip_urls',array('url='.sql_quote($url_segment)));
436                        }
437                        if ($row) {
438                                $type = $row['type'];
439                                $col_id = id_table_objet($type);
440                               
441                                // le plus a droite l'emporte pour des objets presents plusieurs fois dans l'url (ie rubrique)
442                                $contexte[$col_id] = $row['id_objet'];
443
444                                $type_parent = '';
445                                if ($p = url_arbo_parent($type))
446                                        $type_parent=end($p);
447                                // l'entite la plus a droite l'emporte, si le type de son parent a ete vu
448                                // sinon c'est un segment contextuel supplementaire a ignorer
449                                // ex : rub1/article/art1/mot1 : il faut ignorer le mot1, la vrai url est celle de l'article
450                                if (!$entite
451                                  OR isset($parents_vus[$type_parent]))
452                                        $entite = $type;
453
454                                // on note le dernier parent vu de chaque type
455                                $parents_vus[$type] = $col_id;
456                        }
457                        else {
458                                // un segment est inconnu
459                                if ($entite=='' OR $entite=='type_urls') {
460                                        // on genere une 404 comme il faut si on ne sait pas ou aller
461                                        return array(array(),'404');
462                                }
463                                return; // ?
464                        }
465                }
466
467                // gerer le retour depuis des urls propres
468                if (($entite=='' OR $entite=='type_urls')
469                AND $GLOBALS['profondeur_url']<=0){
470                        $urls_anciennes = charger_fonction('propres','urls');
471                        return $urls_anciennes($url_propre, $entite, $contexte);
472                }
473        }
474        if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
475                if ($type)
476                        $entite = objet_type ($type);
477                else {
478                        // Si ca ressemble a une URL d'objet, ce n'est pas la home
479                        // et on provoque un 404
480                        if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
481                                $entite = '404';
482                                $contexte['erreur'] = ''; // qu'afficher ici ?  l'url n'existe pas... on ne sait plus dire de quel type d'objet il s'agit
483                        }
484                }
485        }
486        define('_SET_HTML_BASE',1);
487
488        return array($contexte, $entite, null, null);
489}
490
491?>
Note: See TracBrowser for help on using the repository browser.