source: spip-zone/_core_/plugins/urls_etendues/urls/propres.php

Last change on this file was 113294, checked in by spip.franck@…, 2 months ago

Il parait que le futur c'est maintenant :-D

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