1 | <?php |
---|
2 | |
---|
3 | /***************************************************************************\ |
---|
4 | * SPIP, Systeme de publication pour l'internet * |
---|
5 | * * |
---|
6 | * Copyright (c) 2001-2015 * |
---|
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']:80); |
---|
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://code.spip.net/@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://code.spip.net/@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://code.spip.net/@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 -> $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://code.spip.net/@_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 | // les urls de type /1234 sont interpretees comme urls courte vers article 1234 |
---|
237 | // on les encadre d'un - : /-1234- |
---|
238 | if (is_numeric($url)){ |
---|
239 | $url = "-".$url."-"; |
---|
240 | } |
---|
241 | |
---|
242 | if (!defined('_SET_HTML_BASE') OR !_SET_HTML_BASE) |
---|
243 | // Repositionne l'URL par rapport a la racine du site (#GLOBALS) |
---|
244 | $url = str_repeat('../', $GLOBALS['profondeur_url']).$url; |
---|
245 | else |
---|
246 | $url = _DIR_RACINE . $url; |
---|
247 | } else { |
---|
248 | |
---|
249 | // objet connu mais sans possibilite d'URL lisible, revenir au defaut |
---|
250 | include_spip('base/connect_sql'); |
---|
251 | $id_type = id_table_objet($type); |
---|
252 | $url = _DIR_RACINE . get_spip_script('./')."?"._SPIP_PAGE."=$type&$id_type=$id"; |
---|
253 | } |
---|
254 | |
---|
255 | // Ajouter les args |
---|
256 | if ($args) |
---|
257 | $url .= ((strpos($url, '?')===false) ? '?' : '&') . $args; |
---|
258 | |
---|
259 | // Ajouter l'ancre |
---|
260 | if ($ancre) |
---|
261 | $url .= "#$ancre"; |
---|
262 | |
---|
263 | return $url; |
---|
264 | } |
---|
265 | |
---|
266 | // retrouve le fond et les parametres d'une URL propre |
---|
267 | // ou produit une URL propre si on donne un parametre |
---|
268 | // @return array([contexte],[type],[url_redirect],[fond]) : url decodee |
---|
269 | // http://code.spip.net/@urls_propres_dist |
---|
270 | function urls_propres_dist($i, $entite, $args = '', $ancre = '') { |
---|
271 | |
---|
272 | if (is_numeric($i)) |
---|
273 | return _generer_url_propre($entite, $i, $args, $ancre); |
---|
274 | |
---|
275 | $url = $i; |
---|
276 | $id_objet = $type = 0; |
---|
277 | $url_redirect = null; |
---|
278 | // recuperer les &debut_xx; |
---|
279 | if (is_array($args)) |
---|
280 | $contexte = $args; |
---|
281 | else |
---|
282 | parse_str($args,$contexte); |
---|
283 | |
---|
284 | |
---|
285 | // Migration depuis anciennes URLs ? |
---|
286 | // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23 |
---|
287 | if ($GLOBALS['profondeur_url']<=0 |
---|
288 | AND $_SERVER['REQUEST_METHOD'] != 'POST') { |
---|
289 | include_spip('inc/urls'); |
---|
290 | $r = nettoyer_url_page($i, $contexte); |
---|
291 | if ($r) { |
---|
292 | list($contexte, $type,,, $suite) = $r; |
---|
293 | $_id = id_table_objet($type); |
---|
294 | $id_objet = $contexte[$_id]; |
---|
295 | $url_propre = generer_url_entite($id_objet, $type); |
---|
296 | if (strlen($url_propre) |
---|
297 | AND !strstr($url,$url_propre)) { |
---|
298 | list(,$hash) = array_pad(explode('#', $url_propre), 2, null); |
---|
299 | $args = array(); |
---|
300 | foreach(array_filter(explode('&', $suite)) as $fragment) { |
---|
301 | if ($fragment != "$_id=$id_objet") |
---|
302 | $args[] = $fragment; |
---|
303 | } |
---|
304 | $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash); |
---|
305 | |
---|
306 | return array($contexte, $type, $url_redirect, $type); |
---|
307 | } |
---|
308 | } |
---|
309 | } |
---|
310 | /* Fin compatibilite anciennes urls */ |
---|
311 | |
---|
312 | // Chercher les valeurs d'environnement qui indiquent l'url-propre |
---|
313 | $url_propre = preg_replace(',[?].*,', '', $url); |
---|
314 | |
---|
315 | // Mode Query-String ? |
---|
316 | $is_qs = false; |
---|
317 | if (!$url_propre |
---|
318 | AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) { |
---|
319 | $url_propre = $r[1]; |
---|
320 | $is_qs = true; |
---|
321 | } |
---|
322 | |
---|
323 | if (!$url_propre |
---|
324 | OR $url_propre==_DIR_RESTREINT_ABS |
---|
325 | OR $url_propre==_SPIP_SCRIPT) return; // qu'est-ce qu'il veut ??? |
---|
326 | |
---|
327 | |
---|
328 | // gerer le cas de retour depuis des urls arbos |
---|
329 | // mais si url arbo ne trouve pas, on veut une 404 par securite |
---|
330 | if ($GLOBALS['profondeur_url']>0 AND !defined('_FORCE_URLS_PROPRES')){ |
---|
331 | $urls_anciennes = charger_fonction('arbo','urls'); |
---|
332 | return $urls_anciennes($url_propre, $entite, $contexte); |
---|
333 | } |
---|
334 | |
---|
335 | include_spip('base/abstract_sql'); // chercher dans la table des URLS |
---|
336 | |
---|
337 | // Compatibilite avec propres2 |
---|
338 | $url_propre = preg_replace(',\.html$,i', '', $url_propre); |
---|
339 | |
---|
340 | // Revenir en utf-8 si encodage type %D8%A7 (farsi) |
---|
341 | $url_propre = rawurldecode($url_propre); |
---|
342 | |
---|
343 | // Compatibilite avec les anciens marqueurs d'URL propres |
---|
344 | // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls) |
---|
345 | if (!$row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre, '', 'TEXT'))) { |
---|
346 | // Sinon enlever les marqueurs eventuels |
---|
347 | $url_propre2 = retirer_marqueurs_url_propre($url_propre); |
---|
348 | |
---|
349 | $row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre2, '', 'TEXT')); |
---|
350 | } |
---|
351 | |
---|
352 | if ($row) { |
---|
353 | $type = $row['type']; |
---|
354 | $col_id = id_table_objet($type); |
---|
355 | $contexte[$col_id] = $row['id_objet']; |
---|
356 | $entite = $row['type']; |
---|
357 | |
---|
358 | // Si l'url est vieux, donner le nouveau |
---|
359 | if ($recent = sql_fetsel('url, date', 'spip_urls', |
---|
360 | 'type='.sql_quote($row['type'], '', 'TEXT').' AND id_objet='.sql_quote($row['id_objet']) |
---|
361 | .' AND date>'.sql_quote($row['date'], '', 'TEXT') |
---|
362 | .' AND url<>'.sql_quote($row['url'], '', 'TEXT'), '', 'date DESC', 1)) { |
---|
363 | // Mode compatibilite pour conserver la distinction -Rubrique- |
---|
364 | if (_MARQUEUR_URL) { |
---|
365 | $marqueur = unserialize(_MARQUEUR_URL); |
---|
366 | $marqueur1 = $marqueur[$type.'1']; // debut '+-' |
---|
367 | $marqueur2 = $marqueur[$type.'2']; // fin '-+' |
---|
368 | } else |
---|
369 | $marqueur1 = $marqueur2 = ''; |
---|
370 | $url_redirect = $marqueur1 . $recent['url'] . $marqueur2; |
---|
371 | } |
---|
372 | } |
---|
373 | |
---|
374 | if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) { |
---|
375 | if ($type) { |
---|
376 | $entite = objet_type($type); |
---|
377 | } else { |
---|
378 | // Si ca ressemble a une URL d'objet, ce n'est pas la home |
---|
379 | // et on provoque un 404 |
---|
380 | if (preg_match(',^[^\.]+(\.html)?$,', $url)) { |
---|
381 | $entite = '404'; |
---|
382 | $contexte['erreur'] = ''; |
---|
383 | |
---|
384 | // l'url n'existe pas... |
---|
385 | // on ne sait plus dire de quel type d'objet il s'agit |
---|
386 | // sauf si on a le marqueur. et la c'est un peu sale... |
---|
387 | if (_MARQUEUR_URL) { |
---|
388 | $fmarqueur = @array_flip(unserialize(_MARQUEUR_URL)); |
---|
389 | preg_match(',^([+][-]|[-+@_]),', $url_propre, $regs); |
---|
390 | $objet = $regs ? substr($fmarqueur[$regs[1]],0,n-1) : 'article'; |
---|
391 | $contexte['erreur'] = _T( |
---|
392 | ($objet=='rubrique' OR $objet=='breve') |
---|
393 | ? 'public:aucune_'.$objet |
---|
394 | : 'public:aucun_'.$objet |
---|
395 | ); |
---|
396 | } |
---|
397 | } |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | return array($contexte, $entite, $url_redirect, $is_qs?$entite:null); |
---|
402 | } |
---|
403 | |
---|
404 | ?> |
---|