3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2013 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
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 \***************************************************************************/
13 if (!defined("_ECRIRE_INC_VERSION")) return; // securiser
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');
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);
26 - Comment utiliser ce jeu d'URLs ?
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.
34 Dans les pages de configuration, choisissez 'propres' comme type d'url
36 SPIP calculera alors ses liens sous la forme
37 "Mon-titre-d-article".
39 La variante 'propres2' ajoutera '.html' aux adresses generees :
40 "Mon-titre-d-article.html"
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"
47 if (!defined('_terminaison_urls_propres')) define ('_terminaison_urls_propres', '');
48 if (!defined('_debut_urls_propres')) define ('_debut_urls_propres', '');
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);
59 if (!defined('_url_sep_id')) define('_url_sep_id',_url_propres_sep_id
);
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.
67 if (!defined('_MARQUEUR_URL')) define('_MARQUEUR_URL', serialize(array('rubrique1' => '-', 'rubrique2' => '-', 'breve1' => '+', 'breve2' => '+', 'site1' => '@', 'site2' => '@', 'auteur1' => '_', 'auteur2' => '_', 'mot1' => '+-', 'mot2' => '-+')));
69 // Retire les marqueurs de type dans une URL propre ancienne maniere
71 // http://doc.spip.org/@retirer_marqueurs_url_propre
72 function retirer_marqueurs_url_propre($url_propre) {
73 if (preg_match(',^[+][-](.*?)[-][+]$,', $url_propre, $regs)) {
76 else if (preg_match(',^([-+_@])(.*?)\1?$,', $url_propre, $regs)) {
79 // les articles n'ont pas de marqueur
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'];
94 include_spip('inc/filtres');
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'];
105 // Trouver l'URL associee a la n-ieme cle primaire d'une table SQL
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
116 $id_objet = intval($id_objet);
119 // Recuperer une URL propre correspondant a l'objet.
120 // mais urls a 1 segment uniquement (pas d'urls /)
121 $row = sql_fetsel("U.url, U.date, U.perma, $champ_titre",
122 "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)",
123 "O.$col_id=$id_objet AND (U.segments IS NULL OR U.segments=1)", '', 'U.date DESC', 1);
125 if (!$row) return ""; # Quand $id_objet n'est pas un numero connu
127 $url_propre = $row['url'];
129 // Se contenter de cette URL si elle existe ;
130 // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
132 // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
133 // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
134 // qui requetent en base
135 $modifier_url = (defined('_VAR_URLS') AND _VAR_URLS
AND !$row['perma']);
136 if ($url_propre AND !$modifier_url)
139 // Sinon, creer une URL
140 $url = pipeline('propres_creer_chaine_url',
142 'data' => $url_propre, // le vieux url_propre
143 'objet' => array_merge($row,
144 array('type' => $type, 'id_objet' => $id_objet)
149 // Eviter de tamponner les URLs a l'ancienne (cas d'un article
150 // intitule "auteur2")
151 include_spip('inc/urls');
152 $objets = urls_liste_objets();
153 if (preg_match(',^('.$objets.')[0-9]+$,', $url, $r)
155 $url = $url._url_propres_sep_id
.$id_objet;
157 // Pas de changement d'url
158 if ($url == $url_propre)
161 // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
163 include_spip('inc/autoriser');
164 $modifier_url = autoriser('modifierurl', $type, $id_objet);
167 // Verifier si l'utilisateur veut effectivement changer l'URL
169 AND CONFIRMER_MODIFIER_URL
171 AND $url != preg_replace('/,.*/', '', $url_propre))
176 if ($confirmer AND !_request('ok')) {
177 die ("vous changez d'url ? $url_propre -> $url");
180 $set = array('url' => $url, 'type' => $type, 'id_objet' => $id_objet);
181 include_spip('action/editer_url');
182 if (!url_insert($set,$confirmer,_url_propres_sep_id
))
183 return $url_propre; //serveur out ? retourner au mieux
188 // http://doc.spip.org/@_generer_url_propre
189 function _generer_url_propre($type, $id, $args='', $ancre='') {
191 if ($generer_url_externe = charger_fonction("generer_url_$type",'urls',true)) {
192 $url = $generer_url_externe($id, $args, $ancre);
193 if (NULL != $url) return $url;
196 // Mode compatibilite pour conserver la distinction -Rubrique-
198 $marqueur = unserialize(_MARQUEUR_URL
);
199 $marqueur1 = isset($marqueur[$type.'1']) ?
$marqueur[$type.'1'] : '' ; // debut '+-'
200 $marqueur2 = isset($marqueur[$type.'2']) ?
$marqueur[$type.'2'] : '' ; // fin '-+'
202 $marqueur1 = $marqueur2 = '';
206 $propre = declarer_url_propre($type, $id);
208 if ($propre === false) return ''; // objet inconnu. raccourci ?
211 $url = _debut_urls_propres
215 . _terminaison_urls_propres
;
217 if (!defined('_SET_HTML_BASE') OR !_SET_HTML_BASE
)
218 // Repositionne l'URL par rapport a la racine du site (#GLOBALS)
219 $url = str_repeat('../', $GLOBALS['profondeur_url']).$url;
221 $url = _DIR_RACINE
. $url;
224 // objet connu mais sans possibilite d'URL lisible, revenir au defaut
225 include_spip('base/connect_sql');
226 $id_type = id_table_objet($type);
227 $url = _DIR_RACINE
. get_spip_script('./')."?"._SPIP_PAGE
."=$type&$id_type=$id";
232 $url .= ((strpos($url, '?')===false) ?
'?' : '&') . $args;
241 // retrouve le fond et les parametres d'une URL propre
242 // ou produit une URL propre si on donne un parametre
243 // @return array([contexte],[type],[url_redirect],[fond]) : url decodee
244 // http://doc.spip.org/@urls_propres_dist
245 function urls_propres_dist($i, $entite, $args='', $ancre='') {
248 return _generer_url_propre($entite, $i, $args, $ancre);
251 $id_objet = $type = 0;
252 $url_redirect = null;
253 // recuperer les &debut_xx;
257 parse_str($args,$contexte);
260 // Migration depuis anciennes URLs ?
261 // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
262 if ($GLOBALS['profondeur_url']<=0
263 AND $_SERVER['REQUEST_METHOD'] != 'POST') {
264 include_spip('inc/urls');
265 $r = nettoyer_url_page($i, $contexte);
267 list($contexte, $type,,, $suite) = $r;
268 $_id = id_table_objet($type);
269 $id_objet = $contexte[$_id];
270 $url_propre = generer_url_entite($id_objet, $type);
271 if (strlen($url_propre)
272 AND !strstr($url,$url_propre)) {
273 list(,$hash) = explode('#', $url_propre);
275 foreach(array_filter(explode('&', $suite)) as $fragment) {
276 if ($fragment != "$_id=$id_objet")
279 $url_redirect = generer_url_entite($id_objet, $type, join('&',array_filter($args)), $hash);
281 return array($contexte, $type, $url_redirect, $type);
285 /* Fin compatibilite anciennes urls */
286 // Chercher les valeurs d'environnement qui indiquent l'url-propre
287 if (isset($_SERVER['REDIRECT_url_propre']))
288 $url_propre = $_SERVER['REDIRECT_url_propre'];
289 elseif (isset($_ENV['url_propre']))
290 $url_propre = $_ENV['url_propre'];
292 // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
293 $url = ltrim($url,'/');
294 $url = explode('/',$url);
295 while (count($url)>$GLOBALS['profondeur_url']+
1)
297 $url = implode('/',$url);
298 $url_propre = preg_replace(',[?].*,', '', $url);
301 // Mode Query-String ?
304 AND preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)) {
310 OR $url_propre==_DIR_RESTREINT_ABS
311 OR $url_propre==_SPIP_SCRIPT
) return; // qu'est-ce qu'il veut ???
314 // gerer le cas de retour depuis des urls arbos
315 // mais si url arbo ne trouve pas, on veut une 404 par securite
316 if ($GLOBALS['profondeur_url']>0 AND !defined('_FORCE_URLS_PROPRES')){
317 $urls_anciennes = charger_fonction('arbo','urls');
318 return $urls_anciennes($url_propre, $entite, $contexte);
321 include_spip('base/abstract_sql'); // chercher dans la table des URLS
323 // Compatibilite avec propres2
324 $url_propre = preg_replace(',\.html$,i', '', $url_propre);
326 // Revenir en utf-8 si encodage type %D8%A7 (farsi)
327 $url_propre = rawurldecode($url_propre);
329 // Compatibilite avec les anciens marqueurs d'URL propres
330 // Tester l'entree telle quelle (avec 'url_libre' des sites ont pu avoir des entrees avec marqueurs dans la table spip_urls)
331 if (!$row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre))) {
332 // Sinon enlever les marqueurs eventuels
333 $url_propre2 = retirer_marqueurs_url_propre($url_propre);
335 $row = sql_fetsel('id_objet, type, date, url', 'spip_urls', 'url='.sql_quote($url_propre2));
339 $type = $row['type'];
340 $col_id = id_table_objet($type);
341 $contexte[$col_id] = $row['id_objet'];
342 $entite = $row['type'];
344 // Si l'url est vieux, donner le nouveau
345 if ($recent = sql_fetsel('url, date', 'spip_urls',
346 'type='.sql_quote($row['type']).' AND id_objet='.sql_quote($row['id_objet'])
347 .' AND date>'.sql_quote($row['date'])
348 .' AND url<>'.sql_quote($row['url']), '', 'date DESC', 1)) {
349 // Mode compatibilite pour conserver la distinction -Rubrique-
351 $marqueur = unserialize(_MARQUEUR_URL
);
352 $marqueur1 = $marqueur[$type.'1']; // debut '+-'
353 $marqueur2 = $marqueur[$type.'2']; // fin '-+'
355 $marqueur1 = $marqueur2 = '';
356 $url_redirect = $marqueur1 . $recent['url'] . $marqueur2;
360 if ($entite=='' OR $entite=='type_urls' /* compat .htaccess 2.0 */) {
362 $entite = objet_type($type);
364 // Si ca ressemble a une URL d'objet, ce n'est pas la home
365 // et on provoque un 404
366 if (preg_match(',^.*/[^\.]+(\.html)?$,', $url)) {
368 $contexte['erreur'] = '';
370 // l'url n'existe pas...
371 // on ne sait plus dire de quel type d'objet il s'agit
372 // sauf si on a le marqueur. et la c'est un peu sale...
374 $fmarqueur = @array_flip
(unserialize(_MARQUEUR_URL
));
375 preg_match(',^([+][-]|[-+@_]),', $url_propre, $regs);
376 $objet = $regs ?
substr($fmarqueur[$regs[1]],0,n
-1) : 'article';
377 $contexte['erreur'] = _T(
378 ($objet=='rubrique' OR $objet=='breve')
379 ?
'public:aucune_'.$objet
380 : 'public:aucun_'.$objet
387 return array($contexte, $entite, $url_redirect, $is_qs?
$entite:null);