3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
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;
15 if (!defined('_CONTEXTE_IGNORE_VARIABLES')) define('_CONTEXTE_IGNORE_VARIABLES', "/(^var_|^PHPSESSID$)/");
17 // calcule la page et les entetes
18 // determine le contexte donne par l'URL (en tenant compte des reecritures)
19 // grace a la fonction de passage d'URL a id (reciproque dans urls/*php)
22 // http://doc.spip.org/@assembler
23 function assembler($fond, $connect='') {
25 global $flag_preserver,$lastmodified, $use_cache, $contexte;
27 $contexte = calculer_contexte();
28 $page = array('contexte_implicite'=>calculer_contexte_implicite());
29 $page['contexte_implicite']['cache'] = $fond . preg_replace(',\.[a-zA-Z0-9]*$,', '', preg_replace('/[?].*$/', '', $GLOBALS['REQUEST_URI']));
30 // Cette fonction est utilisee deux fois
31 $cacher = charger_fonction('cacher', 'public');
32 // Les quatre derniers parametres sont modifies par la fonction:
33 // emplacement, validite, et, s'il est valide, contenu & age
34 $res = $cacher($GLOBALS['contexte'], $use_cache, $chemin_cache, $page, $lastmodified);
35 // Si un resultat est retourne, c'est un message d'impossibilite
36 if ($res) {return array('texte' => $res);}
38 if (!$chemin_cache ||
!$lastmodified) $lastmodified = time();
40 $headers_only = ($_SERVER['REQUEST_METHOD'] == 'HEAD');
42 // Pour les pages non-dynamiques (indiquees par #CACHE{duree,cache-client})
43 // une perennite valide a meme reponse qu'une requete HEAD (par defaut les
44 // pages sont dynamiques)
45 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
46 AND !$GLOBALS['var_mode']
48 AND isset($page['entetes'])
49 AND isset($page['entetes']['Cache-Control'])
50 AND strstr($page['entetes']['Cache-Control'],'max-age=')
51 AND !strstr($_SERVER['SERVER_SOFTWARE'],'IIS/')
53 $since = preg_replace('/;.*/', '',
54 $_SERVER['HTTP_IF_MODIFIED_SINCE']);
55 $since = str_replace('GMT', '', $since);
56 if (trim($since) == gmdate("D, d M Y H:i:s", $lastmodified)) {
57 $page['status'] = 304;
62 // Si requete HEAD ou Last-modified compatible, ignorer le texte
63 // et pas de content-type (pour contrer le bouton admin de inc-public)
65 $page['entetes']["Connection"] = "close";
68 // si la page est prise dans le cache
70 // Informer les boutons d'admin du contexte
71 // (fourni par $renommer ci-dessous lors de la mise en cache)
72 $contexte = $page['contexte'];
74 // vider les globales url propres qui ne doivent plus etre utilisees en cas
75 // d'inversion url => objet
76 unset($_SERVER['REDIRECT_url_propre']);
77 unset($_ENV['url_propre']);
79 // ATTENTION, gestion des URLs transformee par le htaccess
80 // $renommer = 'urls_propres_dist';
81 // renvoie array($contexte, $type, $url_redirect, $nfond)
82 // $nfond n'est retourne que si l'url est definie apres le ?
83 // et risque d'etre effacee par un form en get
84 // elle est utilisee par form_hidden exclusivement
85 // Compat ascendante si le retour est null:
86 // 1. $contexte est global car cette fonction le modifie.
87 // 2. $fond est passe par reference, pour la meme raison
88 // et calculer la page
90 $renommer = generer_url_entite();
92 $url = nettoyer_uri();
93 $a = $renommer($url, $fond, $contexte);
95 list($ncontexte, $type, $url_redirect, $nfond) = $a;
96 if (strlen($url_redirect)
97 AND $url !== $url_redirect) {
98 spip_log("Redirige $url vers $url_redirect");
99 include_spip('inc/headers');
100 redirige_par_entete($url_redirect, '', 301);
105 OR $fond == 'type_urls' /* compat avec htaccess 2.0.0 */
107 $fond = ($type === 'syndic') ?
'site' : $type;
108 if (isset($ncontexte))
109 $contexte = $ncontexte;
110 if (defined('_DEFINIR_CONTEXTE_TYPE') AND _DEFINIR_CONTEXTE_TYPE
)
111 $contexte['type'] = ($type === 'syndic') ?
'site' : $type;
114 // compatibilite <= 1.9.2
115 elseif (function_exists('recuperer_parametres_url'))
116 recuperer_parametres_url($fond, nettoyer_uri());
118 // vider les globales url propres qui ne doivent plus etre utilisees en cas
119 // d'inversion url => objet
120 unset($_SERVER['REDIRECT_url_propre']);
121 unset($_ENV['url_propre']);
123 // squelette par defaut
127 // produire la page : peut mettre a jour $lastmodified
128 $produire_page = charger_fonction('produire_page','public');
129 $page = $produire_page($fond, $GLOBALS['contexte'], $use_cache, $chemin_cache, NULL, $page, $lastmodified, $connect);
131 $erreur = _T('info_erreur_squelette2',
132 array('fichier'=>htmlspecialchars($fond).'.'._EXTENSION_SQUELETTES
));
133 erreur_squelette($erreur);
134 // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
135 $page = array('texte' => '', 'erreur' => $erreur);
139 if ($page AND $chemin_cache) $page['cache'] = $chemin_cache;
141 auto_content_type($page);
143 $flag_preserver |
= headers_sent();
145 // Definir les entetes si ce n'est fait
146 if (!$flag_preserver) {
147 if ($GLOBALS['flag_ob']) {
148 // Si la page est vide, produire l'erreur 404 ou message d'erreur pour les inclusions
149 if (trim($page['texte']) === ''
150 AND $GLOBALS['var_mode'] != 'debug'
151 AND !isset($page['entetes']['Location']) // cette page realise une redirection, donc pas d'erreur
153 // passer le type d'objet recherche au contexte de la page d'erreur
154 $contexte['type'] = (isset($type)?
$type:$fond);
155 $page = message_page_indisponible($page, $contexte);
156 // cacher la page d'erreur car celle ci est contextuelle
160 AND $page['entetes']['X-Spip-Cache'] > 0){
161 $cacher = charger_fonction('cacher', 'public');
162 $lastinclude = time();
163 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
166 // pas de cache client en mode 'observation'
167 if ($GLOBALS['var_mode']) {
168 $page['entetes']["Cache-Control"]= "no-cache,must-revalidate";
169 $page['entetes']["Pragma"] = "no-cache";
175 // Entete Last-Modified:
176 // eviter d'etre incoherent en envoyant un lastmodified identique
177 // a celui qu'on a refuse d'honorer plus haut (cf. #655)
179 AND !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
180 AND !isset($page['entetes']["Last-Modified"]))
181 $page['entetes']["Last-Modified"]=gmdate("D, d M Y H:i:s", $lastmodified)." GMT";
187 // Contexte : lors du calcul d'une page spip etablit le contexte a partir
188 // des variables $_GET et $_POST, purgees des fausses variables var_*
189 // Note : pour hacker le contexte depuis le fichier d'appel (page.php),
190 // il est recommande de modifier $_GET['toto'] (meme si la page est
191 // appelee avec la methode POST).
193 // http://doc.spip.org/@calculer_contexte
194 function calculer_contexte() {
197 foreach($_GET as $var => $val) {
198 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
199 $contexte[$var] = $val;
201 foreach($_POST as $var => $val) {
202 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
203 $contexte[$var] = $val;
210 * Calculer le contexte implicite, qui n'apparait pas dans le ENV d'un cache
211 * mais est utilise pour distinguer deux caches differents
213 * @staticvar string $notes
216 function calculer_contexte_implicite(){
217 static $notes = null;
219 $notes = charger_fonction('notes','inc');
220 $contexte_implicite = array(
221 'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ?
222 'host' => $_SERVER['HTTP_HOST'],
223 'https' => $_SERVER['HTTPS'],
224 'espace' => test_espace_prive(),
225 'marqueur' => (isset($GLOBALS['marqueur']) ?
$GLOBALS['marqueur'] : ''),
226 'notes' => $notes('','contexter_cache'),
228 return $contexte_implicite;
232 // fonction pour compatibilite arriere, probablement superflue
235 // http://doc.spip.org/@auto_content_type
236 function auto_content_type($page)
238 global $flag_preserver;
239 if (!isset($flag_preserver))
241 $flag_preserver = ($page && preg_match("/header\s*\(\s*.content\-type:/isx",$page['texte']) ||
(isset($page['entetes']['Content-Type'])));
245 // http://doc.spip.org/@inclure_page
246 function inclure_page($fond, $contexte, $connect='') {
248 global $lastmodified;
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 $cacher = charger_fonction('cacher', 'public');
257 // Les quatre derniers parametres sont modifies par la fonction:
258 // emplacement, validite, et, s'il est valide, contenu & age
259 $res = $cacher($contexte, $use_cache, $chemin_cache, $page, $lastinclude);
260 // $res = message d'erreur : on sort de la
261 if ($res) {return array('texte' => $res);}
263 // Si use_cache ne vaut pas 0, la page doit etre calculee
264 // produire la page : peut mettre a jour $lastinclude
265 // le contexte_cache envoye a cacher() a ete conserve et est passe a produire
267 $produire_page = charger_fonction('produire_page','public');
268 $page = $produire_page($fond, $contexte, $use_cache, $chemin_cache, $contexte, $page, $lastinclude, $connect);
270 // dans tous les cas, mettre a jour $lastmodified
271 $lastmodified = max($lastmodified, $lastinclude);
277 * Produire la page et la mettre en cache
278 * lorsque c'est necessaire
280 * @param string $fond
281 * @param array $contexte
282 * @param int $use_cache
283 * @param string $chemin_cache
284 * @param array $contexte_cache
286 * @param int $lastinclude
287 * @param string $connect
290 function public_produire_page_dist($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, &$page, &$lastinclude, $connect=''){
292 $parametrer = charger_fonction('parametrer', 'public');
293 $page = $parametrer($fond, $contexte, $chemin_cache, $connect);
294 // et on l'enregistre sur le disque
299 AND $page['entetes']['X-Spip-Cache'] > 0){
300 $cacher = charger_fonction('cacher', 'public');
301 $lastinclude = time();
302 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
308 // Fonction inseree par le compilateur dans le code compile.
309 // Elle recoit un contexte pour inclure un squelette,
310 // et les valeurs du contexte de compil prepare par memoriser_contexte_compil
311 // elle-meme appelee par calculer_balise_dynamique dans references.php:
318 function inserer_balise_dynamique($contexte_exec, $contexte_compil)
320 if (!is_array($contexte_exec))
321 echo $contexte_exec; // message d'erreur etc
323 inclure_balise_dynamique($contexte_exec, true, $contexte_compil);
327 // Attention, un appel explicite a cette fonction suppose certains include
328 // $echo = faut-il faire echo ou return
330 // http://doc.spip.org/@inclure_balise_dynamique
331 function inclure_balise_dynamique($texte, $echo=true, $contexte_compil=array())
333 if (is_array($texte)) {
335 list($fond, $delainc, $contexte_inclus) = $texte;
337 // delais a l'ancienne, c'est pratiquement mort
338 $d = isset($GLOBALS['delais']) ?
$GLOBALS['delais'] : NULL;
339 $GLOBALS['delais'] = $delainc;
341 $page = recuperer_fond($fond,$contexte_inclus,array('trim'=>false, 'raw' => true, 'compil' => $contexte_compil));
343 $texte = $page['texte'];
345 $GLOBALS['delais'] = $d;
346 // Faire remonter les entetes
347 if (is_array($page['entetes'])) {
349 unset($page['entetes']['X-Spip-Cache']);
350 unset($page['entetes']['Content-Type']);
351 if (isset($GLOBALS['page']) AND is_array($GLOBALS['page'])) {
352 if (!is_array($GLOBALS['page']['entetes']))
353 $GLOBALS['page']['entetes'] = array();
354 $GLOBALS['page']['entetes'] =
355 array_merge($GLOBALS['page']['entetes'],$page['entetes']);
358 // on se refere a $page['contexte'] a la place
359 if (isset($page['contexte']['_pipeline'])) {
360 $pipe = is_array($page['contexte']['_pipeline'])?
reset($page['contexte']['_pipeline']):$page['contexte']['_pipeline'];
361 $args = is_array($page['contexte']['_pipeline'])?
end($page['contexte']['_pipeline']):array();
362 $args['contexte'] = $page['contexte'];
363 unset($args['contexte']['_pipeline']); // par precaution, meme si le risque de boucle infinie est a priori nul
364 if (isset($GLOBALS['spip_pipeline'][$pipe]))
365 $texte = pipeline($pipe,array(
371 if ($GLOBALS['var_mode'] == 'debug') {
372 // compatibilite : avant on donnait le numero de ligne ou rien.
373 $ligne = intval(isset($contexte_compil[3]) ?
$contexte_compil[3] : $contexte_compil);
374 $GLOBALS['debug_objets']['resultat'][$ligne] = $texte;
383 // Traiter var_recherche ou le referrer pour surligner les mots
384 // http://doc.spip.org/@f_surligne
385 function f_surligne ($texte) {
386 if (!$GLOBALS['html']) return $texte;
387 $rech = _request('var_recherche');
388 if (!$rech AND !isset($_SERVER['HTTP_REFERER'])) return $texte;
389 include_spip('inc/surligne');
390 return surligner_mots($texte, $rech);
393 // Valider/indenter a la demande.
394 // http://doc.spip.org/@f_tidy
395 function f_tidy ($texte) {
398 if ($xhtml # tidy demande
399 AND $GLOBALS['html'] # verifie que la page avait l'entete text/html
401 AND !headers_sent()) {
402 # Compatibilite ascendante
403 if (!is_string($xhtml)) $xhtml ='tidy';
405 if (!$f = charger_fonction($xhtml, 'inc', true)) {
406 spip_log("tidy absent, l'indenteur SPIP le remplace");
407 $f = charger_fonction('sax', 'xml');
415 // Offre #INSERT_HEAD sur tous les squelettes (bourrin)
416 // a activer dans mes_options via :
417 // $spip_pipeline['affichage_final'] .= '|f_insert_head';
418 // http://doc.spip.org/@f_insert_head
419 function f_insert_head($texte) {
420 if (!$GLOBALS['html']) return $texte;
421 include_spip('public/admin'); // pour strripos
423 ($pos = stripos($texte, '</head>'))
424 ||
($pos = stripos($texte, '<body>'))
427 if (false === strpos(substr($texte, 0,$pos), '<!-- insert_head -->')) {
428 $insert = "\n".pipeline('insert_head','<!-- f_insert_head -->')."\n";
429 $texte = substr_replace($texte, $insert, $pos, 0);
435 // Inserer au besoin les boutons admins
436 // http://doc.spip.org/@f_admin
437 function f_admin ($texte) {
438 if (!$GLOBALS['html']) return $texte;
439 if ($GLOBALS['affiche_boutons_admin']) {
440 include_spip('public/admin');
441 $texte = affiche_boutons_admin($texte);
443 if (_request('var_mode')=='noajax'){
444 $texte = preg_replace(',(class=[\'"][^\'"]*)ajax([^\'"]*[\'"]),Uims',"\\1\\2",$texte);
450 // http://doc.spip.org/@message_page_indisponible
451 function message_page_indisponible ($page, $contexte) {
452 static $deja = false;
453 if ($deja) return "erreur";
455 '404' => '404 Not Found',
456 '503' => '503 Service Unavailable',
459 $contexte['status'] = ($page !== false) ?
'404' : '503';
460 $contexte['code'] = $codes[$contexte['status']];
461 $contexte['fond'] = '404'; // gere les 2 erreurs
462 $contexte['erreur'] = _T($erreur);
463 if (!isset($contexte['lang']))
464 $contexte['lang'] = $GLOBALS['spip_lang'];
467 // passer aux plugins qui peuvent decider d'une page d'erreur plus pertinent
468 // ex restriction d'acces => 401
469 $contexte = pipeline('page_indisponible',$contexte);
471 // produire la page d'erreur
472 $page = inclure_page($contexte['fond'], $contexte);
474 $page = inclure_page('404', $contexte);
475 $page['status'] = $contexte['status'];
479 // temporairement ici : a mettre dans le futur inc/modeles
480 // creer_contexte_de_modele('left', 'autostart=true', ...) renvoie un array()
481 // http://doc.spip.org/@creer_contexte_de_modele
482 function creer_contexte_de_modele($args) {
484 foreach ($args as $var=>$val) {
485 if (is_int($var)){ // argument pas formate
486 if (in_array($val, array('left', 'right', 'center'))) {
488 $contexte[$var] = $val;
490 $args = explode('=', $val);
491 if (count($args)>=2) // Flashvars=arg1=machin&arg2=truc genere plus de deux args
492 $contexte[trim($args[0])] = substr($val,strlen($args[0])+
1);
493 else // notation abregee
494 $contexte[trim($val)] = trim($val);
498 $contexte[$var] = $val;
504 // Calcule le modele et retourne la mini-page ainsi calculee
505 // http://doc.spip.org/@inclure_modele
506 function inclure_modele($type, $id, $params, $lien, $connect='') {
509 if (++
$compteur>10) return ''; # ne pas boucler indefiniment
511 $type = strtolower($type);
515 $params = array_filter(explode('|', $params));
517 list(,$soustype) = each($params);
518 $soustype = strtolower($soustype);
519 if (in_array($soustype,
520 array('left', 'right', 'center', 'ajax'))) {
521 list(,$soustype) = each($params);
522 $soustype = strtolower($soustype);
525 if (preg_match(',^[a-z0-9_]+$,', $soustype)) {
526 if (!trouve_modele($fond = ($type.'_'.$soustype))) {
530 // enlever le sous type des params
531 $params = array_diff($params,array($soustype));
535 // Si ca marche pas en precisant le sous-type, prendre le type
536 if (!$fond AND !trouve_modele($fond = $type))
538 $fond = 'modeles/'.$fond;
541 '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 // Le numero du modele est mis dans l'environnement
544 // d'une part sous l'identifiant "id"
545 // et d'autre part sous l'identifiant de la cle primaire supposee
546 // par la fonction table_objet,
547 // qui ne marche vraiment que pour les tables std de SPIP
548 // (<site1> =>> site =>> id_syndic =>> id_syndic=1)
549 $_id = 'id_' . table_objet($type);
550 if (preg_match('/s$/',$_id)) $_id = substr($_id,0,-1);
551 $contexte['id'] = $contexte[$_id] = $id;
554 $contexte['class'] = $class;
556 // Si un lien a ete passe en parametre, ex: [<modele1>->url]
558 # un eventuel guillemet (") sera reechappe par #ENV
559 $contexte['lien'] = str_replace(""",'"', $lien['href']);
560 $contexte['lien_class'] = $lien['class'];
561 $contexte['lien_mime'] = $lien['mime'];
564 // Traiter les parametres
565 // par exemple : <img1|center>, <emb12|autostart=true> ou <doc1|lang=en>
566 $arg_list = creer_contexte_de_modele($params);
567 $contexte['args'] = $arg_list; // on passe la liste des arguments du modeles dans une variable args
568 $contexte = array_merge($contexte,$arg_list);
571 // Appliquer le modele avec le contexte
572 $retour = recuperer_fond($fond, $contexte, array(), $connect);
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')).' ',
580 $retour = inserer_attribut($retour, 'class',
581 trim(str_replace(' spip_lien_ok ', ' ', " $classes ")));
583 $retour = "<a href='".$lien['href']."' class='".$lien['class']."'>".$retour."</a>";
586 return (isset($arg_list['ajax'])AND $arg_list['ajax']=='ajax')
587 ?
encoder_contexte_ajax($contexte,'',$retour)
591 // Un inclure_page qui marche aussi pour l'espace prive
592 // fonction interne a spip, ne pas appeler directement
593 // pour recuperer $page complet, utiliser:
594 // recuperer_fond($fond,$contexte,array('raw'=>true))
595 // http://doc.spip.org/@evaluer_fond
596 function evaluer_fond ($fond, $contexte=array(), $connect=null) {
598 $page = inclure_page($fond, $contexte, $connect);
600 if (!$page) return $page;
602 if ($page['process_ins'] != 'html') {
603 // restaurer l'etat des notes
604 if (isset($page['notes']) AND $page['notes']){
605 $notes = charger_fonction("notes","inc");
606 $notes($page['notes'],'restaurer_etat');
610 xml_hack($page, true);
611 eval('?' . '>' . $page['texte']);
612 $page['texte'] = ob_get_contents();
614 $page['process_ins'] = 'html';
617 page_base_href($page['texte']);
619 // Lever un drapeau (global) si le fond utilise #SESSION
620 // a destination de public/parametrer
621 // pour remonter vers les inclusions appelantes
622 // il faut bien lever ce drapeau apres avoir evalue le fond
623 // pour ne pas faire descendre le flag vers les inclusions appelees
624 if (isset($page['invalideurs'])
625 AND isset($page['invalideurs']['session']))
626 $GLOBALS['cache_utilise_session'] = $page['invalideurs']['session'];
632 // Appeler avant et apres chaque eval()
633 // http://doc.spip.org/@xml_hack
634 function xml_hack(&$page, $echap = false) {
636 $page['texte'] = str_replace('<'.'?xml', "<\1?xml", $page['texte']);
638 $page['texte'] = str_replace("<\1?xml", '<'.'?xml', $page['texte']);
641 // http://doc.spip.org/@page_base_href
642 function page_base_href(&$texte){
643 if (!defined('_SET_HTML_BASE'))
644 // si la profondeur est superieure a 1
645 // est que ce n'est pas une url page ni une url action
646 // activer par defaut
647 define('_SET_HTML_BASE',
648 $GLOBALS['profondeur_url'] >= (_DIR_RESTREINT?
1:2)
649 AND _request(_SPIP_PAGE
) !== 'login'
650 AND !_request('action'));
653 AND isset($GLOBALS['html']) AND $GLOBALS['html']
654 AND $GLOBALS['profondeur_url']>0
655 AND ($poshead = strpos($texte,'</head>'))!==FALSE){
656 $head = substr($texte,0,$poshead);
658 if (strpos($head, '<base')===false)
661 // si aucun <base ...> n'a de href c'est bon quand meme !
663 include_spip('inc/filtres');
664 $bases = extraire_balises($head,'base');
665 foreach ($bases as $base)
666 if (extraire_attribut($base,'href'))
670 include_spip('inc/filtres_mini');
671 // ajouter un base qui reglera tous les liens relatifs
672 $base = url_absolue('./');
673 $bbase = "\n<base href=\"$base\" />";
674 if (($pos = strpos($head, '<head>')) !== false)
675 $head = substr_replace($head, $bbase, $pos+
6, 0);
676 elseif(preg_match(",<head[^>]*>,i",$head,$r)){
677 $head = str_replace($r[0], $r[0].$bbase, $head);
679 $texte = $head . substr($texte,$poshead);
681 $base = $_SERVER['REQUEST_URI'];
682 if (strpos($texte,"href='#")!==false)
683 $texte = str_replace("href='#","href='$base#",$texte);
684 if (strpos($texte, "href=\"#")!==false)
685 $texte = str_replace("href=\"#","href=\"$base#",$texte);
690 function public_previsualisation_dist($page)
692 if (preg_match(',^\s*text/html,',$page['entetes']['Content-Type'])) {
693 include_spip('inc/filtres'); // pour http_img_pack
694 $x = _T('previsualisation');
695 $x = http_img_pack('naviguer-site.png', $x) . ' ' . majuscules($x);
696 $x = "<div class='spip-previsu'>$x</div>";
697 if (!$pos = strpos($page['texte'], '</body>'))
698 $pos = strlen($page['texte']);
699 $page['texte'] = substr_replace($page['texte'], $x, $pos, 0);
704 // Envoyer les entetes, en retenant ceux qui sont a usage interne
705 // et demarrent par X-Spip-...
706 // http://doc.spip.org/@envoyer_entetes
707 function envoyer_entetes($entetes) {
708 foreach ($entetes as $k => $v)
709 # if (strncmp($k, 'X-Spip-', 7))
710 @header
(strlen($v)?
"$k: $v":$k);