[SPIP] v3.2.1-->v3.2.2
[lhc/web/www.git] / www / ecrire / public / assembler.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 * Ce fichier regroupe les fonctions permettant de calculer la page et les entĂȘtes
15 *
16 * Determine le contexte donne par l'URL (en tenant compte des reecritures)
17 * grace a la fonction de passage d'URL a id (reciproque dans urls/*php)
18 *
19 * @package SPIP\Core\Compilateur\Assembler
20 **/
21
22 if (!defined('_ECRIRE_INC_VERSION')) {
23 return;
24 }
25
26 if (!defined('_CONTEXTE_IGNORE_VARIABLES')) {
27 define('_CONTEXTE_IGNORE_VARIABLES', "/(^var_|^PHPSESSID$|^fbclid$|^utm_)/");
28 }
29
30 // http://code.spip.net/@assembler
31 function assembler($fond, $connect = '') {
32
33 // flag_preserver est modifie ici, et utilise en globale
34 // use_cache sert a informer le bouton d'admin pr savoir s'il met un *
35 // contexte est utilise en globale dans le formulaire d'admin
36
37 $GLOBALS['contexte'] = calculer_contexte();
38 $page = array('contexte_implicite' => calculer_contexte_implicite());
39 $page['contexte_implicite']['cache'] = $fond . preg_replace(',\.[a-zA-Z0-9]*$,', '',
40 preg_replace('/[?].*$/', '', $GLOBALS['REQUEST_URI']));
41 // Cette fonction est utilisee deux fois
42 $cacher = charger_fonction('cacher', 'public', true);
43 // Les quatre derniers parametres sont modifies par la fonction:
44 // emplacement, validite, et, s'il est valide, contenu & age
45 if ($cacher) {
46 $res = $cacher($GLOBALS['contexte'], $GLOBALS['use_cache'], $chemin_cache, $page, $lastmodified);
47 } else {
48 $GLOBALS['use_cache'] = -1;
49 }
50 // Si un resultat est retourne, c'est un message d'impossibilite
51 if ($res) {
52 return array('texte' => $res);
53 }
54
55 if (!$chemin_cache || !$lastmodified) {
56 $lastmodified = time();
57 }
58
59 $headers_only = ($_SERVER['REQUEST_METHOD'] == 'HEAD');
60 $calculer_page = true;
61
62 // Pour les pages non-dynamiques (indiquees par #CACHE{duree,cache-client})
63 // une perennite valide a meme reponse qu'une requete HEAD (par defaut les
64 // pages sont dynamiques)
65 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
66 and (!defined('_VAR_MODE') or !_VAR_MODE)
67 and $chemin_cache
68 and isset($page['entetes'])
69 and isset($page['entetes']['Cache-Control'])
70 and strstr($page['entetes']['Cache-Control'], 'max-age=')
71 and !strstr($_SERVER['SERVER_SOFTWARE'], 'IIS/')
72 ) {
73 $since = preg_replace('/;.*/', '',
74 $_SERVER['HTTP_IF_MODIFIED_SINCE']);
75 $since = str_replace('GMT', '', $since);
76 if (trim($since) == gmdate("D, d M Y H:i:s", $lastmodified)) {
77 $page['status'] = 304;
78 $headers_only = true;
79 $calculer_page = false;
80 }
81 }
82
83 // Si requete HEAD ou Last-modified compatible, ignorer le texte
84 // et pas de content-type (pour contrer le bouton admin de inc-public)
85 if (!$calculer_page) {
86 $page['texte'] = "";
87 } else {
88 // si la page est prise dans le cache
89 if (!$GLOBALS['use_cache']) {
90 // Informer les boutons d'admin du contexte
91 // (fourni par urls_decoder_url ci-dessous lors de la mise en cache)
92 $GLOBALS['contexte'] = $page['contexte'];
93
94 // vider les globales url propres qui ne doivent plus etre utilisees en cas
95 // d'inversion url => objet
96 // plus necessaire si on utilise bien la fonction urls_decoder_url
97 #unset($_SERVER['REDIRECT_url_propre']);
98 #unset($_ENV['url_propre']);
99 } else {
100 // Compat ascendante :
101 // 1. $contexte est global
102 // (a evacuer car urls_decoder_url gere ce probleme ?)
103 // et calculer la page
104 if (!test_espace_prive()) {
105 include_spip('inc/urls');
106 list($fond, $GLOBALS['contexte'], $url_redirect) = urls_decoder_url(nettoyer_uri(), $fond, $GLOBALS['contexte'],
107 true);
108 }
109 // squelette par defaut
110 if (!strlen($fond)) {
111 $fond = 'sommaire';
112 }
113
114 // produire la page : peut mettre a jour $lastmodified
115 $produire_page = charger_fonction('produire_page', 'public');
116 $page = $produire_page($fond, $GLOBALS['contexte'], $GLOBALS['use_cache'], $chemin_cache, null, $page,
117 $lastmodified, $connect);
118 if ($page === '') {
119 $erreur = _T('info_erreur_squelette2',
120 array('fichier' => spip_htmlspecialchars($fond) . '.' . _EXTENSION_SQUELETTES));
121 erreur_squelette($erreur);
122 // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
123 $page = array('texte' => '', 'erreur' => $erreur);
124 }
125 }
126
127 if ($page and $chemin_cache) {
128 $page['cache'] = $chemin_cache;
129 }
130
131 auto_content_type($page);
132
133 $GLOBALS['flag_preserver'] |= headers_sent();
134
135 // Definir les entetes si ce n'est fait
136 if (!$GLOBALS['flag_preserver']) {
137 if ($GLOBALS['flag_ob']) {
138 // Si la page est vide, produire l'erreur 404 ou message d'erreur pour les inclusions
139 if (trim($page['texte']) === ''
140 and _VAR_MODE != 'debug'
141 and !isset($page['entetes']['Location']) // cette page realise une redirection, donc pas d'erreur
142 ) {
143 $GLOBALS['contexte']['fond_erreur'] = $fond;
144 $page = message_page_indisponible($page, $GLOBALS['contexte']);
145 }
146 // pas de cache client en mode 'observation'
147 if (defined('_VAR_MODE') and _VAR_MODE) {
148 $page['entetes']["Cache-Control"] = "no-cache,must-revalidate";
149 $page['entetes']["Pragma"] = "no-cache";
150 }
151 }
152 }
153 }
154
155 // Entete Last-Modified:
156 // eviter d'etre incoherent en envoyant un lastmodified identique
157 // a celui qu'on a refuse d'honorer plus haut (cf. #655)
158 if ($lastmodified
159 and !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
160 and !isset($page['entetes']["Last-Modified"])
161 ) {
162 $page['entetes']["Last-Modified"] = gmdate("D, d M Y H:i:s", $lastmodified) . " GMT";
163 }
164
165 // fermer la connexion apres les headers si requete HEAD
166 if ($headers_only) {
167 $page['entetes']["Connection"] = "close";
168 }
169
170 return $page;
171 }
172
173 /**
174 * Calcul le contexte de la page
175 *
176 * lors du calcul d'une page spip etablit le contexte a partir
177 * des variables $_GET et $_POST, purgees des fausses variables var_*
178 *
179 * Note : pour hacker le contexte depuis le fichier d'appel (page.php),
180 * il est recommande de modifier $_GET['toto'] (meme si la page est
181 * appelee avec la methode POST).
182 *
183 * http://code.spip.net/@calculer_contexte
184 *
185 * @return array Un tableau du contexte de la page
186 */
187 function calculer_contexte() {
188
189 $contexte = array();
190 foreach ($_GET as $var => $val) {
191 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES, $var)) {
192 $contexte[$var] = $val;
193 }
194 }
195 foreach ($_POST as $var => $val) {
196 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES, $var)) {
197 $contexte[$var] = $val;
198 }
199 }
200
201 return $contexte;
202 }
203
204 /**
205 * Calculer le contexte implicite, qui n'apparait pas dans le ENV d'un cache
206 * mais est utilise pour distinguer deux caches differents
207 *
208 * @staticvar string $notes
209 * @return array
210 */
211 function calculer_contexte_implicite() {
212 static $notes = null;
213 if (is_null($notes)) {
214 $notes = charger_fonction('notes', 'inc', true);
215 }
216 $contexte_implicite = array(
217 'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ?
218 'host' => (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null),
219 'https' => (isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : ''),
220 'espace' => test_espace_prive(),
221 'marqueur' => (isset($GLOBALS['marqueur']) ? $GLOBALS['marqueur'] : ''),
222 'marqueur_skel' => (isset($GLOBALS['marqueur_skel']) ? $GLOBALS['marqueur_skel'] : ''),
223 'notes' => $notes ? $notes('', 'contexter_cache') : '',
224 'spip_version_code' => $GLOBALS['spip_version_code'],
225 );
226 if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
227 $contexte_implicite['host'] .= "|" . $_SERVER['HTTP_X_FORWARDED_HOST'];
228 }
229
230 return $contexte_implicite;
231 }
232
233 //
234 // fonction pour compatibilite arriere, probablement superflue
235 //
236
237 // http://code.spip.net/@auto_content_type
238 function auto_content_type($page) {
239
240 if (!isset($GLOBALS['flag_preserver'])) {
241 $GLOBALS['flag_preserver'] = ($page && preg_match("/header\s*\(\s*.content\-type:/isx",
242 $page['texte']) || (isset($page['entetes']['Content-Type'])));
243 }
244 }
245
246 // http://code.spip.net/@inclure_page
247 function inclure_page($fond, $contexte, $connect = '') {
248 static $cacher, $produire_page;
249
250 // enlever le fond de contexte inclus car sinon il prend la main
251 // dans les sous inclusions -> boucle infinie d'inclusion identique
252 // (cette precaution n'est probablement plus utile)
253 unset($contexte['fond']);
254 $page = array('contexte_implicite' => calculer_contexte_implicite());
255 $page['contexte_implicite']['cache'] = $fond;
256 if (is_null($cacher)) {
257 $cacher = charger_fonction('cacher', 'public', true);
258 }
259 // Les quatre derniers parametres sont modifies par la fonction:
260 // emplacement, validite, et, s'il est valide, contenu & age
261 if ($cacher) {
262 $res = $cacher($contexte, $use_cache, $chemin_cache, $page, $lastinclude);
263 } else {
264 $use_cache = -1;
265 }
266 // $res = message d'erreur : on sort de la
267 if ($res) {
268 return array('texte' => $res);
269 }
270
271 // Si use_cache ne vaut pas 0, la page doit etre calculee
272 // produire la page : peut mettre a jour $lastinclude
273 // le contexte_cache envoye a cacher() a ete conserve et est passe a produire
274 if ($use_cache) {
275 if (is_null($produire_page)) {
276 $produire_page = charger_fonction('produire_page', 'public');
277 }
278 $page = $produire_page($fond, $contexte, $use_cache, $chemin_cache, $contexte, $page, $lastinclude, $connect);
279 }
280 // dans tous les cas, mettre a jour $GLOBALS['lastmodified']
281 $GLOBALS['lastmodified'] = max((isset($GLOBALS['lastmodified']) ? $GLOBALS['lastmodified'] : 0), $lastinclude);
282
283 return $page;
284 }
285
286 /**
287 * Produire la page et la mettre en cache
288 * lorsque c'est necessaire
289 *
290 * @param string $fond
291 * @param array $contexte
292 * @param int $use_cache
293 * @param string $chemin_cache
294 * @param array $contexte_cache
295 * @param array $page
296 * @param int $lastinclude
297 * @param string $connect
298 * @return array
299 */
300 function public_produire_page_dist(
301 $fond,
302 $contexte,
303 $use_cache,
304 $chemin_cache,
305 $contexte_cache,
306 &$page,
307 &$lastinclude,
308 $connect = ''
309 ) {
310 static $parametrer, $cacher;
311 if (!$parametrer) {
312 $parametrer = charger_fonction('parametrer', 'public');
313 }
314 $page = $parametrer($fond, $contexte, $chemin_cache, $connect);
315 // et on l'enregistre sur le disque
316 if ($chemin_cache
317 and $use_cache > -1
318 and is_array($page)
319 and count($page)
320 and isset($page['entetes']['X-Spip-Cache'])
321 and $page['entetes']['X-Spip-Cache'] > 0
322 ) {
323 if (is_null($cacher)) {
324 $cacher = charger_fonction('cacher', 'public', true);
325 }
326 $lastinclude = time();
327 if ($cacher) {
328 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
329 } else {
330 $use_cache = -1;
331 }
332 }
333
334 return $page;
335 }
336
337 // Fonction inseree par le compilateur dans le code compile.
338 // Elle recoit un contexte pour inclure un squelette,
339 // et les valeurs du contexte de compil prepare par memoriser_contexte_compil
340 // elle-meme appelee par calculer_balise_dynamique dans references.php:
341 // 0: sourcefile
342 // 1: codefile
343 // 2: id_boucle
344 // 3: ligne
345 // 4: langue
346
347 function inserer_balise_dynamique($contexte_exec, $contexte_compil) {
348 if (!is_array($contexte_exec)) {
349 echo $contexte_exec;
350 } // message d'erreur etc
351 else {
352 inclure_balise_dynamique($contexte_exec, true, $contexte_compil);
353 }
354 }
355
356 /**
357 * Inclusion de balise dynamique
358 * Attention, un appel explicite a cette fonction suppose certains include
359 *
360 * http://code.spip.net/@inclure_balise_dynamique
361 *
362 * @param string|array $texte
363 * @param bool $echo Faut-il faire echo ou return
364 * @param array $contexte_compil Contexte de la compilation
365 * @return string
366 */
367 function inclure_balise_dynamique($texte, $echo = true, $contexte_compil = array()) {
368 if (is_array($texte)) {
369
370 list($fond, $delainc, $contexte_inclus) = $texte;
371
372 // delais a l'ancienne, c'est pratiquement mort
373 $d = isset($GLOBALS['delais']) ? $GLOBALS['delais'] : null;
374 $GLOBALS['delais'] = $delainc;
375
376 $page = recuperer_fond($fond, $contexte_inclus,
377 array('trim' => false, 'raw' => true, 'compil' => $contexte_compil));
378
379 $texte = $page['texte'];
380
381 $GLOBALS['delais'] = $d;
382 // Faire remonter les entetes
383 if (is_array($page['entetes'])) {
384 // mais pas toutes
385 unset($page['entetes']['X-Spip-Cache']);
386 unset($page['entetes']['Content-Type']);
387 if (isset($GLOBALS['page']) and is_array($GLOBALS['page'])) {
388 if (!is_array($GLOBALS['page']['entetes'])) {
389 $GLOBALS['page']['entetes'] = array();
390 }
391 $GLOBALS['page']['entetes'] =
392 array_merge($GLOBALS['page']['entetes'], $page['entetes']);
393 }
394 }
395 // _pipelines au pluriel array('nom_pipeline' => $args...) avec une syntaxe permettant plusieurs pipelines
396 if (isset($page['contexte']['_pipelines'])
397 and is_array($page['contexte']['_pipelines'])
398 and count($page['contexte']['_pipelines'])
399 ) {
400 foreach ($page['contexte']['_pipelines'] as $pipe => $args) {
401 $args['contexte'] = $page['contexte'];
402 unset($args['contexte']['_pipelines']); // par precaution, meme si le risque de boucle infinie est a priori nul
403 $texte = pipeline(
404 $pipe,
405 array(
406 'data' => $texte,
407 'args' => $args
408 ),
409 false
410 );
411 }
412 }
413 }
414
415 if (defined('_VAR_MODE') and _VAR_MODE == 'debug') {
416 // compatibilite : avant on donnait le numero de ligne ou rien.
417 $ligne = intval(isset($contexte_compil[3]) ? $contexte_compil[3] : $contexte_compil);
418 $GLOBALS['debug_objets']['resultat'][$ligne] = $texte;
419 }
420 if ($echo) {
421 echo $texte;
422 } else {
423 return $texte;
424 }
425 }
426
427 // http://code.spip.net/@message_page_indisponible
428 function message_page_indisponible($page, $contexte) {
429 static $deja = false;
430 if ($deja) {
431 return "erreur";
432 }
433 $codes = array(
434 '404' => '404 Not Found',
435 '503' => '503 Service Unavailable',
436 );
437
438 $contexte['status'] = ($page !== false) ? '404' : '503';
439 $contexte['code'] = $codes[$contexte['status']];
440 $contexte['fond'] = '404'; // gere les 2 erreurs
441 if (!isset($contexte['lang'])) {
442 $contexte['lang'] = $GLOBALS['spip_lang'];
443 }
444
445 $deja = true;
446 // passer aux plugins qui peuvent decider d'une page d'erreur plus pertinent
447 // ex restriction d'acces => 401
448 $contexte = pipeline('page_indisponible', $contexte);
449
450 // produire la page d'erreur
451 $page = inclure_page($contexte['fond'], $contexte);
452 if (!$page) {
453 $page = inclure_page('404', $contexte);
454 }
455 $page['status'] = $contexte['status'];
456
457 return $page;
458 }
459
460 // temporairement ici : a mettre dans le futur inc/modeles
461 // creer_contexte_de_modele('left', 'autostart=true', ...) renvoie un array()
462 // http://code.spip.net/@creer_contexte_de_modele
463 function creer_contexte_de_modele($args) {
464 $contexte = array();
465 foreach ($args as $var => $val) {
466 if (is_int($var)) { // argument pas formate
467 if (in_array($val, array('left', 'right', 'center'))) {
468 $var = 'align';
469 $contexte[$var] = $val;
470 } else {
471 $args = explode('=', $val);
472 if (count($args) >= 2) // Flashvars=arg1=machin&arg2=truc genere plus de deux args
473 {
474 $contexte[trim($args[0])] = substr($val, strlen($args[0]) + 1);
475 } else // notation abregee
476 {
477 $contexte[trim($val)] = trim($val);
478 }
479 }
480 } else {
481 $contexte[$var] = $val;
482 }
483 }
484
485 return $contexte;
486 }
487
488 /**
489 * Calcule le modele et retourne la mini-page ainsi calculee
490 *
491 * http://code.spip.net/@inclure_modele
492 *
493 * @param $type string Nom du modele
494 * @param $id int
495 * @param $params array ParamĂštres du modĂšle
496 * @param $lien array Informations du lien entourant l'appel du modÚle en base de données
497 * @param $connect string
498 * @param $env array
499 * @staticvar string $compteur
500 * @return string
501 */
502 function inclure_modele($type, $id, $params, $lien, $connect = '', $env = array()) {
503
504 static $compteur;
505 if (++$compteur > 10) {
506 return '';
507 } # ne pas boucler indefiniment
508
509 $type = strtolower($type);
510
511 $fond = $class = '';
512
513 $params = array_filter(explode('|', $params));
514 if ($params) {
515 list(, $soustype) = each($params);
516 $soustype = strtolower(trim($soustype));
517 if (in_array($soustype,
518 array('left', 'right', 'center', 'ajax'))) {
519 list(, $soustype) = each($params);
520 $soustype = strtolower($soustype);
521 }
522
523 if (preg_match(',^[a-z0-9_]+$,', $soustype)) {
524 if (!trouve_modele($fond = ($type . '_' . $soustype))) {
525 $fond = '';
526 $class = $soustype;
527 }
528 // enlever le sous type des params
529 $params = array_diff($params, array($soustype));
530 }
531 }
532
533 // Si ca marche pas en precisant le sous-type, prendre le type
534 if (!$fond and !trouve_modele($fond = $type)) {
535 spip_log("Modele $type introuvable", _LOG_INFO_IMPORTANTE);
536
537 return false;
538 }
539 $fond = 'modeles/' . $fond;
540 // Creer le contexte
541 $contexte = $env;
542 $contexte['dir_racine'] = _DIR_RACINE; # eviter de mixer un cache racine et un cache ecrire (meme si pour l'instant les modeles ne sont pas caches, le resultat etant different il faut que le contexte en tienne compte
543
544 // Le numero du modele est mis dans l'environnement
545 // d'une part sous l'identifiant "id"
546 // et d'autre part sous l'identifiant de la cle primaire
547 // par la fonction id_table_objet,
548 // (<article1> =>> article =>> id_article =>> id_article=1)
549 $_id = id_table_objet($type);
550 $contexte['id'] = $contexte[$_id] = $id;
551
552 if (isset($class)) {
553 $contexte['class'] = $class;
554 }
555
556 // Si un lien a ete passe en parametre, ex: [<modele1>->url] ou [<modele1|title_du_lien{hreflang}->url]
557 if ($lien) {
558 # un eventuel guillemet (") sera reechappe par #ENV
559 $contexte['lien'] = str_replace("&quot;", '"', $lien['href']);
560 $contexte['lien_class'] = $lien['class'];
561 $contexte['lien_mime'] = $lien['mime'];
562 $contexte['lien_title'] = $lien['title'];
563 $contexte['lien_hreflang'] = $lien['hreflang'];
564 }
565
566 // Traiter les parametres
567 // par exemple : <img1|center>, <emb12|autostart=true> ou <doc1|lang=en>
568 $arg_list = creer_contexte_de_modele($params);
569 $contexte['args'] = $arg_list; // on passe la liste des arguments du modeles dans une variable args
570 $contexte = array_merge($contexte, $arg_list);
571
572 // Appliquer le modele avec le contexte
573 $retour = recuperer_fond($fond, $contexte, array(), $connect);
574
575 // Regarder si le modele tient compte des liens (il *doit* alors indiquer
576 // spip_lien_ok dans les classes de son conteneur de premier niveau ;
577 // sinon, s'il y a un lien, on l'ajoute classiquement
578 if (strstr(' ' . ($classes = extraire_attribut($retour, 'class')) . ' ',
579 'spip_lien_ok')) {
580 $retour = inserer_attribut($retour, 'class',
581 trim(str_replace(' spip_lien_ok ', ' ', " $classes ")));
582 } else {
583 if ($lien) {
584 $retour = "<a href='" . $lien['href'] . "' class='" . $lien['class'] . "'>" . $retour . "</a>";
585 }
586 }
587
588 $compteur--;
589
590 return (isset($arg_list['ajax']) and $arg_list['ajax'] == 'ajax')
591 ? encoder_contexte_ajax($contexte, '', $retour)
592 : $retour;
593 }
594
595 // Un inclure_page qui marche aussi pour l'espace prive
596 // fonction interne a spip, ne pas appeler directement
597 // pour recuperer $page complet, utiliser:
598 // recuperer_fond($fond,$contexte,array('raw'=>true))
599 // http://code.spip.net/@evaluer_fond
600 function evaluer_fond($fond, $contexte = array(), $connect = null) {
601
602 $page = inclure_page($fond, $contexte, $connect);
603
604 if (!$page) {
605 return $page;
606 }
607 // eval $page et affecte $res
608 include _ROOT_RESTREINT . "public/evaluer_page.php";
609
610 // Lever un drapeau (global) si le fond utilise #SESSION
611 // a destination de public/parametrer
612 // pour remonter vers les inclusions appelantes
613 // il faut bien lever ce drapeau apres avoir evalue le fond
614 // pour ne pas faire descendre le flag vers les inclusions appelees
615 if (isset($page['invalideurs'])
616 and isset($page['invalideurs']['session'])
617 ) {
618 $GLOBALS['cache_utilise_session'] = $page['invalideurs']['session'];
619 }
620
621 return $page;
622 }
623
624
625 // http://code.spip.net/@page_base_href
626 function page_base_href(&$texte) {
627 static $set_html_base = null;
628 if (is_null($set_html_base)) {
629 if (!defined('_SET_HTML_BASE'))
630 // si la profondeur est superieure a 1
631 // est que ce n'est pas une url page ni une url action
632 // activer par defaut
633 {
634 $set_html_base = ((
635 $GLOBALS['profondeur_url'] >= (_DIR_RESTREINT ? 1 : 2)
636 and _request(_SPIP_PAGE) !== 'login'
637 and !_request('action')) ? true : false);
638 } else {
639 $set_html_base = _SET_HTML_BASE;
640 }
641 }
642
643 if ($set_html_base
644 and isset($GLOBALS['html']) and $GLOBALS['html']
645 and $GLOBALS['profondeur_url'] > 0
646 and ($poshead = strpos($texte, '</head>')) !== false
647 ) {
648 $head = substr($texte, 0, $poshead);
649 $insert = false;
650 if (strpos($head, '<base') === false) {
651 $insert = true;
652 } else {
653 // si aucun <base ...> n'a de href c'est bon quand meme !
654 $insert = true;
655 include_spip('inc/filtres');
656 $bases = extraire_balises($head, 'base');
657 foreach ($bases as $base) {
658 if (extraire_attribut($base, 'href')) {
659 $insert = false;
660 }
661 }
662 }
663 if ($insert) {
664 include_spip('inc/filtres_mini');
665 // ajouter un base qui reglera tous les liens relatifs
666 $base = url_absolue('./');
667 $bbase = "\n<base href=\"$base\" />";
668 if (($pos = strpos($head, '<head>')) !== false) {
669 $head = substr_replace($head, $bbase, $pos + 6, 0);
670 } elseif (preg_match(",<head[^>]*>,i", $head, $r)) {
671 $head = str_replace($r[0], $r[0] . $bbase, $head);
672 }
673 $texte = $head . substr($texte, $poshead);
674 // gerer les ancres
675 $base = $_SERVER['REQUEST_URI'];
676 // pas de guillemets ni < dans l'URL qu'on insere dans le HTML
677 if (strpos($base,"'") or strpos($base,'"') or strpos($base,'<')) {
678 $base = str_replace(array("'",'"','<'),array("%27",'%22','%3C'), $base);
679 }
680 if (strpos($texte, "href='#") !== false) {
681 $texte = str_replace("href='#", "href='$base#", $texte);
682 }
683 if (strpos($texte, "href=\"#") !== false) {
684 $texte = str_replace("href=\"#", "href=\"$base#", $texte);
685 }
686 }
687 }
688 }
689
690
691 // Envoyer les entetes, en retenant ceux qui sont a usage interne
692 // et demarrent par X-Spip-...
693 // http://code.spip.net/@envoyer_entetes
694 function envoyer_entetes($entetes) {
695 foreach ($entetes as $k => $v) # if (strncmp($k, 'X-Spip-', 7))
696 {
697 @header(strlen($v) ? "$k: $v" : $k);
698 }
699 }