[SPIP] v3.2.1-->v3.2.2
[lhc/web/www.git] / www / ecrire / inc / urls.php
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
13
14 /**
15 * Gestion des URLS
16 *
17 * @package SPIP\Core\URLs
18 **/
19
20 if (!defined('_ECRIRE_INC_VERSION')) {
21 return;
22 }
23 include_spip('base/objets');
24
25 /**
26 * Décoder une URL en utilisant les fonctions inverses
27 *
28 * Gère les URLs transformées par le htaccess.
29 *
30 * @note
31 * `$renommer = 'urls_propres_dist';`
32 * renvoie `array($contexte, $type, $url_redirect, $nfond)`
33 *
34 * `$nfond` n'est retourné que si l'URL est définie apres le `?`
35 * et risque d'être effacée par un form en get.
36 * Elle est utilisée par form_hidden exclusivement.
37 *
38 * Compat ascendante si le retour est NULL en gérant une sauvegarde/restauration
39 * des globales modifiées par les anciennes fonctions
40 *
41 * @param string $url
42 * URL à décoder
43 * @param string $fond
44 * Fond initial par défaut
45 * @param array $contexte
46 * Contexte initial à prendre en compte
47 * @param bool $assembler
48 * `true` si l'URL correspond à l'URL principale de la page qu'on est en train d'assembler
49 * dans ce cas la fonction redirigera automatiquement si besoin
50 * et utilisera les eventuelles globales `$_SERVER['REDIRECT_url_propre']` et `$_ENV['url_propre']`
51 * provenant du htaccess
52 * @return array
53 * Liste `$fond, $contexte, $url_redirect`.
54 *
55 * Si l'url n'est pas valide, $fond restera à la valeur initiale passée.
56 * Il suffit d'appeler la fonction sans $fond et de vérifier qu'à son retour celui-ci
57 * est non vide pour vérifier une URL
58 *
59 */
60 function urls_decoder_url($url, $fond = '', $contexte = array(), $assembler = false) {
61 static $current_base = null;
62
63 // les anciennes fonctions modifient directement les globales
64 // on les sauve avant l'appel, et on les retablit apres !
65 $save = array(
66 isset($GLOBALS['fond']) ? $GLOBALS['fond'] : null,
67 isset($GLOBALS['contexte']) ? $GLOBALS['contexte'] : null,
68 isset($_SERVER['REDIRECT_url_propre']) ? $_SERVER['REDIRECT_url_propre'] : null,
69 isset($_ENV['url_propre']) ? $_ENV['url_propre'] : null,
70 $GLOBALS['profondeur_url']
71 );
72
73 if (is_null($current_base)) {
74 include_spip('inc/filtres_mini');
75 // le decodage des urls se fait toujours par rapport au site public
76 $current_base = url_absolue(_DIR_RACINE ? _DIR_RACINE : './');
77 }
78 if (strncmp($url, $current_base, strlen($current_base)) == 0) {
79 $url = substr($url, strlen($current_base));
80 }
81
82 // si on est en train d'assembler la page principale,
83 // recuperer l'url depuis les globales url propres si fournies
84 // sinon extraire la bonne portion d'url
85 if ($assembler) {
86 if (isset($_SERVER['REDIRECT_url_propre'])) {
87 $url = $_SERVER['REDIRECT_url_propre'];
88 } elseif (isset($_ENV['url_propre'])) {
89 $url = $_ENV['url_propre'];
90 } else {
91 $qs = explode("?", $url);
92 // ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
93 $url = ltrim($qs[0], '/');
94 $url = explode('/', $url);
95 while (count($url) > $GLOBALS['profondeur_url'] + 1) {
96 array_shift($url);
97 }
98 $qs[0] = implode('/', $url);
99 $url = implode("?", $qs);
100 }
101 }
102
103 unset($_SERVER['REDIRECT_url_propre']);
104 unset($_ENV['url_propre']);
105 include_spip('inc/filtres_mini');
106 if (strpos($url, "://") === false) {
107 $GLOBALS['profondeur_url'] = substr_count(ltrim(resolve_path("/$url"), '/'), '/');
108 } else {
109 $GLOBALS['profondeur_url'] = max(0, substr_count($url, "/") - substr_count($current_base, "/"));
110 }
111
112 $url_redirect = "";
113 $renommer = generer_url_entite('', '', '', '', true);
114 if (!$renommer and !function_exists('recuperer_parametres_url')) {
115 $renommer = charger_fonction('page', 'urls');
116 } // fallback pour decoder l'url
117 if ($renommer) {
118 $a = $renommer($url, $fond, $contexte);
119 if (is_array($a)) {
120 list($ncontexte, $type, $url_redirect, $nfond) = array_pad($a, 4, null);
121 if ($url_redirect == $url) {
122 $url_redirect = "";
123 } // securite pour eviter une redirection infinie
124 if ($assembler and strlen($url_redirect)) {
125 spip_log("Redirige $url vers $url_redirect");
126 include_spip('inc/headers');
127 redirige_par_entete($url_redirect, '', 301);
128 }
129 if (isset($nfond)) {
130 $fond = $nfond;
131 } else {
132 if ($fond == ''
133 or $fond == 'type_urls' /* compat avec htaccess 2.0.0 */
134 ) {
135 $fond = $type;
136 }
137 }
138 if (isset($ncontexte)) {
139 $contexte = $ncontexte;
140 }
141 if (defined('_DEFINIR_CONTEXTE_TYPE') and _DEFINIR_CONTEXTE_TYPE) {
142 $contexte['type'] = $type;
143 }
144 if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') and _DEFINIR_CONTEXTE_TYPE_PAGE) {
145 $contexte['type-page'] = $type;
146 }
147 }
148 } // compatibilite <= 1.9.2
149 elseif (function_exists('recuperer_parametres_url')) {
150 $GLOBALS['fond'] = $fond;
151 $GLOBALS['contexte'] = $contexte;
152 recuperer_parametres_url($fond, nettoyer_uri());
153 // fond est en principe modifiee directement
154 $contexte = $GLOBALS['contexte'];
155 }
156
157 // retablir les globales
158 list($GLOBALS['fond'], $GLOBALS['contexte'], $_SERVER['REDIRECT_url_propre'], $_ENV['url_propre'], $GLOBALS['profondeur_url']) = $save;
159
160 // vider les globales url propres qui ne doivent plus etre utilisees en cas
161 // d'inversion url => objet
162 // maintenir pour compat ?
163 #if ($assembler) {
164 # unset($_SERVER['REDIRECT_url_propre']);
165 # unset($_ENV['url_propre']);
166 #}
167
168 return array($fond, $contexte, $url_redirect);
169 }
170
171
172 /**
173 * Lister les objets pris en compte dans les URLs
174 * c'est à dire suceptibles d'avoir une URL propre
175 *
176 * @param bool $preg
177 * Permet de définir si la fonction retourne une chaine avec `|` comme séparateur
178 * pour utiliser en preg, ou un array()
179 * @return string|array
180 */
181 function urls_liste_objets($preg = true) {
182 static $url_objets = null;
183 if (is_null($url_objets)) {
184 $url_objets = array();
185 // recuperer les tables_objets_sql declarees
186 $tables_objets = lister_tables_objets_sql();
187 foreach ($tables_objets as $t => $infos) {
188 if ($infos['page']) {
189 $url_objets[] = $infos['type'];
190 $url_objets = array_merge($url_objets, $infos['type_surnoms']);
191 }
192 }
193 $url_objets = pipeline('declarer_url_objets', $url_objets);
194 }
195 if (!$preg) {
196 return $url_objets;
197 }
198
199 return implode('|', array_map('preg_quote', $url_objets));
200 }
201
202 /**
203 * Nettoyer une URL, en repérant notamment les raccourcis d'objets
204 *
205 * Repère les entités comme `?article13`, `?rubrique21` ...
206 * les traduisant pour compléter le contexte fourni en entrée
207 *
208 * @param string $url
209 * @param array $contexte
210 * @return array
211 */
212 function nettoyer_url_page($url, $contexte = array()) {
213 $url_objets = urls_liste_objets();
214 $raccourci_url_page_html = ',^(?:[^?]*/)?(' . $url_objets . ')([0-9]+)(?:\.html)?([?&].*)?$,';
215 $raccourci_url_page_id = ',^(?:[^?]*/)?(' . $url_objets . ')\.php3?[?]id_\1=([0-9]+)([?&].*)?$,';
216 $raccourci_url_page_spip = ',^(?:[^?]*/)?(?:spip[.]php)?[?](' . $url_objets . ')([0-9]+)=?(&.*)?$,';
217
218 if (preg_match($raccourci_url_page_html, $url, $regs)
219 or preg_match($raccourci_url_page_id, $url, $regs)
220 or preg_match($raccourci_url_page_spip, $url, $regs)
221 ) {
222 $regs = array_pad($regs, 4, null);
223 $type = objet_type($regs[1]);
224 $_id = id_table_objet($type);
225 $contexte[$_id] = $regs[2];
226 $suite = $regs[3];
227
228 return array($contexte, $type, null, $type, $suite);
229 }
230
231 return array();
232 }
233
234 /**
235 * Générer l'URL d'un objet dans l'espace privé
236 *
237 * L'URL est calculée en fonction de son état publié ou non,
238 * calculé à partir de la déclaration de statut.
239 *
240 * @param string $objet Type d'objet
241 * @param int $id Identifiant de l'objet
242 * @param string $args
243 * @param string $ancre
244 * @param bool|null $public
245 * @param string $connect
246 * @return string
247 *
248 */
249 function generer_url_ecrire_objet($objet, $id, $args = '', $ancre = '', $public = null, $connect = '') {
250 static $furls = array();
251 if (!isset($furls[$objet])) {
252 if (function_exists($f = 'generer_url_ecrire_' . $objet)
253 // ou definie par un plugin
254 or $f = charger_fonction($f, 'urls', true)
255 ) {
256 $furls[$objet] = $f;
257 } else {
258 $furls[$objet] = '';
259 }
260 }
261 if ($furls[$objet]) {
262 return $furls[$objet]($id, $args, $ancre, $public, $connect);
263 }
264 // si pas de flag public fourni
265 // le calculer en fonction de la declaration de statut
266 if (is_null($public) and !$connect) {
267 $public = objet_test_si_publie($objet, $id, $connect);
268 }
269 if ($public or $connect) {
270 return generer_url_entite_absolue($id, $objet, $args, $ancre, $connect);
271 }
272 $a = id_table_objet($objet) . "=" . intval($id);
273 if (!function_exists('objet_info')) {
274 include_spip('inc/filtres');
275 }
276
277 return generer_url_ecrire(objet_info($objet, 'url_voir'), $a . ($args ? "&$args" : '')) . ($ancre ? "#$ancre" : '');
278 }