3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2011 *
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_squelette(_T('info_erreur_squelette2',
132 array('fichier'=>htmlspecialchars($fond).'.'._EXTENSION_SQUELETTES
)));
135 if ($page AND $chemin_cache) $page['cache'] = $chemin_cache;
137 auto_content_type($page);
139 $flag_preserver |
= headers_sent();
141 // Definir les entetes si ce n'est fait
142 if (!$flag_preserver) {
143 if ($GLOBALS['flag_ob']) {
144 // Si la page est vide, produire l'erreur 404 ou message d'erreur pour les inclusions
145 if (trim($page['texte']) === ''
146 AND $GLOBALS['var_mode'] != 'debug'
147 AND !isset($page['entetes']['Location']) // cette page realise une redirection, donc pas d'erreur
149 // passer le type d'objet recherche au contexte de la page d'erreur
150 $contexte['type'] = (isset($type)?
$type:$fond);
151 $page = message_page_indisponible($page, $contexte);
152 // cacher la page d'erreur car celle ci est contextuelle
156 AND $page['entetes']['X-Spip-Cache'] > 0){
157 $cacher = charger_fonction('cacher', 'public');
158 $lastinclude = time();
159 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
162 // pas de cache client en mode 'observation'
163 if ($GLOBALS['var_mode']) {
164 $page['entetes']["Cache-Control"]= "no-cache,must-revalidate";
165 $page['entetes']["Pragma"] = "no-cache";
171 // Entete Last-Modified:
172 // eviter d'etre incoherent en envoyant un lastmodified identique
173 // a celui qu'on a refuse d'honorer plus haut (cf. #655)
175 AND !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
176 AND !isset($page['entetes']["Last-Modified"]))
177 $page['entetes']["Last-Modified"]=gmdate("D, d M Y H:i:s", $lastmodified)." GMT";
183 // Contexte : lors du calcul d'une page spip etablit le contexte a partir
184 // des variables $_GET et $_POST, purgees des fausses variables var_*
185 // Note : pour hacker le contexte depuis le fichier d'appel (page.php),
186 // il est recommande de modifier $_GET['toto'] (meme si la page est
187 // appelee avec la methode POST).
189 // http://doc.spip.org/@calculer_contexte
190 function calculer_contexte() {
193 foreach($_GET as $var => $val) {
194 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
195 $contexte[$var] = $val;
197 foreach($_POST as $var => $val) {
198 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
199 $contexte[$var] = $val;
206 * Calculer le contexte implicite, qui n'apparait pas dans le ENV d'un cache
207 * mais est utilise pour distinguer deux caches differents
209 * @staticvar string $notes
212 function calculer_contexte_implicite(){
213 static $notes = null;
215 $notes = charger_fonction('notes','inc');
216 $contexte_implicite = array(
217 'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ?
218 'host' => $_SERVER['HTTP_HOST'],
219 'https' => $_SERVER['HTTPS'],
220 'espace' => test_espace_prive(),
221 'marqueur' => (isset($GLOBALS['marqueur']) ?
$GLOBALS['marqueur'] : ''),
222 'notes' => $notes('','contexter_cache'),
224 return $contexte_implicite;
228 // fonction pour compatibilite arriere, probablement superflue
231 // http://doc.spip.org/@auto_content_type
232 function auto_content_type($page)
234 global $flag_preserver;
235 if (!isset($flag_preserver))
237 $flag_preserver = ($page && preg_match("/header\s*\(\s*.content\-type:/isx",$page['texte']) ||
(isset($page['entetes']['Content-Type'])));
241 // http://doc.spip.org/@inclure_page
242 function inclure_page($fond, $contexte, $connect='') {
244 global $lastmodified;
246 // enlever le fond de contexte inclus car sinon il prend la main
247 // dans les sous inclusions -> boucle infinie d'inclusion identique
248 // (cette precaution n'est probablement plus utile)
249 unset($contexte['fond']);
250 $page = array('contexte_implicite'=>calculer_contexte_implicite());
251 $page['contexte_implicite']['cache'] = $fond;
252 $cacher = charger_fonction('cacher', 'public');
253 // Les quatre derniers parametres sont modifies par la fonction:
254 // emplacement, validite, et, s'il est valide, contenu & age
255 $res = $cacher($contexte, $use_cache, $chemin_cache, $page, $lastinclude);
256 // $res = message d'erreur : on sort de la
257 if ($res) {return array('texte' => $res);}
259 // Si use_cache ne vaut pas 0, la page doit etre calculee
260 // produire la page : peut mettre a jour $lastinclude
261 // le contexte_cache envoye a cacher() a ete conserve et est passe a produire
263 $produire_page = charger_fonction('produire_page','public');
264 $page = $produire_page($fond, $contexte, $use_cache, $chemin_cache, $contexte, $page, $lastinclude, $connect);
266 // dans tous les cas, mettre a jour $lastmodified
267 $lastmodified = max($lastmodified, $lastinclude);
273 * Produire la page et la mettre en cache
274 * lorsque c'est necessaire
276 * @param string $fond
277 * @param array $contexte
278 * @param int $use_cache
279 * @param string $chemin_cache
280 * @param array $contexte_cache
282 * @param int $lastinclude
283 * @param string $connect
286 function public_produire_page_dist($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, &$page, &$lastinclude, $connect=''){
288 $parametrer = charger_fonction('parametrer', 'public');
289 $page = $parametrer($fond, $contexte, $chemin_cache, $connect);
290 // et on l'enregistre sur le disque
295 AND $page['entetes']['X-Spip-Cache'] > 0){
296 $cacher = charger_fonction('cacher', 'public');
297 $lastinclude = time();
298 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
304 // Fonction inseree par le compilateur dans le code compile.
305 // Elle recoit un contexte pour inclure un squelette,
306 // et les valeurs du contexte de compil prepare par memoriser_contexte_compil
307 // elle-meme appelee par calculer_balise_dynamique dans references.php:
314 function inserer_balise_dynamique($contexte_exec, $contexte_compil)
316 if (!is_array($contexte_exec))
317 echo $contexte_exec; // message d'erreur etc
319 inclure_balise_dynamique($contexte_exec, true, $contexte_compil);
323 // Attention, un appel explicite a cette fonction suppose certains include
324 // $echo = faut-il faire echo ou return
326 // http://doc.spip.org/@inclure_balise_dynamique
327 function inclure_balise_dynamique($texte, $echo=true, $contexte_compil=array())
329 if (is_array($texte)) {
331 list($fond, $delainc, $contexte_inclus) = $texte;
333 // delais a l'ancienne, c'est pratiquement mort
334 $d = isset($GLOBALS['delais']) ?
$GLOBALS['delais'] : NULL;
335 $GLOBALS['delais'] = $delainc;
337 $page = recuperer_fond($fond,$contexte_inclus,array('trim'=>false, 'raw' => true, 'compil' => $contexte_compil));
339 $texte = $page['texte'];
341 $GLOBALS['delais'] = $d;
342 // Faire remonter les entetes
343 if (is_array($page['entetes'])) {
345 unset($page['entetes']['X-Spip-Cache']);
346 unset($page['entetes']['Content-Type']);
347 if (isset($GLOBALS['page']) AND is_array($GLOBALS['page'])) {
348 if (!is_array($GLOBALS['page']['entetes']))
349 $GLOBALS['page']['entetes'] = array();
350 $GLOBALS['page']['entetes'] =
351 array_merge($GLOBALS['page']['entetes'],$page['entetes']);
354 // on se refere a $page['contexte'] a la place
355 if (isset($page['contexte']['_pipeline'])) {
356 $pipe = is_array($page['contexte']['_pipeline'])?
reset($page['contexte']['_pipeline']):$page['contexte']['_pipeline'];
357 $args = is_array($page['contexte']['_pipeline'])?
end($page['contexte']['_pipeline']):array();
358 $args['contexte'] = $page['contexte'];
359 unset($args['contexte']['_pipeline']); // par precaution, meme si le risque de boucle infinie est a priori nul
360 if (isset($GLOBALS['spip_pipeline'][$pipe]))
361 $texte = pipeline($pipe,array(
367 if ($GLOBALS['var_mode'] == 'debug') {
368 // compatibilite : avant on donnait le numero de ligne ou rien.
369 $ligne = intval(isset($contexte_compil[3]) ?
$contexte_compil[3] : $contexte_compil);
370 $GLOBALS['debug_objets']['resultat'][$ligne] = $texte;
379 // Traiter var_recherche ou le referrer pour surligner les mots
380 // http://doc.spip.org/@f_surligne
381 function f_surligne ($texte) {
382 if (!$GLOBALS['html']) return $texte;
383 $rech = _request('var_recherche');
384 if (!$rech AND !isset($_SERVER['HTTP_REFERER'])) return $texte;
385 include_spip('inc/surligne');
386 return surligner_mots($texte, $rech);
389 // Valider/indenter a la demande.
390 // http://doc.spip.org/@f_tidy
391 function f_tidy ($texte) {
394 if ($xhtml # tidy demande
395 AND $GLOBALS['html'] # verifie que la page avait l'entete text/html
397 AND !headers_sent()) {
398 # Compatibilite ascendante
399 if (!is_string($xhtml)) $xhtml ='tidy';
401 if (!$f = charger_fonction($xhtml, 'inc', true)) {
402 spip_log("tidy absent, l'indenteur SPIP le remplace");
403 $f = charger_fonction('sax', 'xml');
411 // Offre #INSERT_HEAD sur tous les squelettes (bourrin)
412 // a activer dans mes_options via :
413 // $spip_pipeline['affichage_final'] .= '|f_insert_head';
414 // http://doc.spip.org/@f_insert_head
415 function f_insert_head($texte) {
416 if (!$GLOBALS['html']) return $texte;
417 include_spip('public/admin'); // pour strripos
419 ($pos = stripos($texte, '</head>'))
420 ||
($pos = stripos($texte, '<body>'))
423 if (false === strpos(substr($texte, 0,$pos), '<!-- insert_head -->')) {
424 $insert = "\n".pipeline('insert_head','<!-- f_insert_head -->')."\n";
425 $texte = substr_replace($texte, $insert, $pos, 0);
431 // Inserer au besoin les boutons admins
432 // http://doc.spip.org/@f_admin
433 function f_admin ($texte) {
434 if ($GLOBALS['affiche_boutons_admin']) {
435 include_spip('public/admin');
436 $texte = affiche_boutons_admin($texte);
438 if (_request('var_mode')=='noajax'){
439 $texte = preg_replace(',(class=[\'"][^\'"]*)ajax([^\'"]*[\'"]),Uims',"\\1\\2",$texte);
445 // http://doc.spip.org/@message_page_indisponible
446 function message_page_indisponible ($page, $contexte) {
447 static $deja = false;
448 if ($deja) return "erreur";
450 '404' => '404 Not Found',
451 '503' => '503 Service Unavailable',
454 $contexte['status'] = ($page !== false) ?
'404' : '503';
455 $contexte['code'] = $codes[$contexte['status']];
456 $contexte['fond'] = '404'; // gere les 2 erreurs
457 $contexte['erreur'] = _T($erreur);
458 if (!isset($contexte['lang']))
459 $contexte['lang'] = $GLOBALS['spip_lang'];
462 // passer aux plugins qui peuvent decider d'une page d'erreur plus pertinent
463 // ex restriction d'acces => 401
464 $contexte = pipeline('page_indisponible',$contexte);
466 // produire la page d'erreur
467 $page = inclure_page($contexte['fond'], $contexte);
469 $page = inclure_page('404', $contexte);
470 $page['status'] = $contexte['status'];
474 // temporairement ici : a mettre dans le futur inc/modeles
475 // creer_contexte_de_modele('left', 'autostart=true', ...) renvoie un array()
476 // http://doc.spip.org/@creer_contexte_de_modele
477 function creer_contexte_de_modele($args) {
479 foreach ($args as $var=>$val) {
480 if (is_int($var)){ // argument pas formate
481 if (in_array($val, array('left', 'right', 'center'))) {
483 $contexte[$var] = $val;
485 $args = explode('=', $val);
486 if (count($args)>=2) // Flashvars=arg1=machin&arg2=truc genere plus de deux args
487 $contexte[trim($args[0])] = substr($val,strlen($args[0])+
1);
488 else // notation abregee
489 $contexte[trim($val)] = trim($val);
493 $contexte[$var] = $val;
499 // Calcule le modele et retourne la mini-page ainsi calculee
500 // http://doc.spip.org/@inclure_modele
501 function inclure_modele($type, $id, $params, $lien, $connect='') {
504 if (++
$compteur>10) return ''; # ne pas boucler indefiniment
506 $type = strtolower($type);
510 $params = array_filter(explode('|', $params));
512 list(,$soustype) = each($params);
513 $soustype = strtolower($soustype);
514 if (in_array($soustype,
515 array('left', 'right', 'center', 'ajax'))) {
516 list(,$soustype) = each($params);
517 $soustype = strtolower($soustype);
520 if (preg_match(',^[a-z0-9_]+$,', $soustype)) {
521 if (!trouve_modele($fond = ($type.'_'.$soustype))) {
525 // enlever le sous type des params
526 $params = array_diff($params,array($soustype));
530 // Si ca marche pas en precisant le sous-type, prendre le type
531 if (!$fond AND !trouve_modele($fond = $type))
533 $fond = 'modeles/'.$fond;
536 '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
538 // Le numero du modele est mis dans l'environnement
539 // d'une part sous l'identifiant "id"
540 // et d'autre part sous l'identifiant de la cle primaire supposee
541 // par la fonction table_objet,
542 // qui ne marche vraiment que pour les tables std de SPIP
543 // (<site1> =>> site =>> id_syndic =>> id_syndic=1)
544 $_id = 'id_' . table_objet($type);
545 if (preg_match('/s$/',$_id)) $_id = substr($_id,0,-1);
546 $contexte['id'] = $contexte[$_id] = $id;
549 $contexte['class'] = $class;
551 // Si un lien a ete passe en parametre, ex: [<modele1>->url]
553 # un eventuel guillemet (") sera reechappe par #ENV
554 $contexte['lien'] = str_replace(""",'"', $lien['href']);
555 $contexte['lien_class'] = $lien['class'];
556 $contexte['lien_mime'] = $lien['mime'];
559 // Traiter les parametres
560 // par exemple : <img1|center>, <emb12|autostart=true> ou <doc1|lang=en>
561 $arg_list = creer_contexte_de_modele($params);
562 $contexte['args'] = $arg_list; // on passe la liste des arguments du modeles dans une variable args
563 $contexte = array_merge($contexte,$arg_list);
566 // Appliquer le modele avec le contexte
567 $retour = recuperer_fond($fond, $contexte, array(), $connect);
570 // Regarder si le modele tient compte des liens (il *doit* alors indiquer
571 // spip_lien_ok dans les classes de son conteneur de premier niveau ;
572 // sinon, s'il y a un lien, on l'ajoute classiquement
573 if (strstr(' ' . ($classes = extraire_attribut($retour, 'class')).' ',
575 $retour = inserer_attribut($retour, 'class',
576 trim(str_replace(' spip_lien_ok ', ' ', " $classes ")));
578 $retour = "<a href='".$lien['href']."' class='".$lien['class']."'>".$retour."</a>";
581 return (isset($arg_list['ajax'])AND $arg_list['ajax']=='ajax')
582 ?
encoder_contexte_ajax($contexte,'',$retour)
586 // Un inclure_page qui marche aussi pour l'espace prive
587 // fonction interne a spip, ne pas appeler directement
588 // pour recuperer $page complet, utiliser:
589 // recuperer_fond($fond,$contexte,array('raw'=>true))
590 // http://doc.spip.org/@evaluer_fond
591 function evaluer_fond ($fond, $contexte=array(), $connect=null) {
593 $page = inclure_page($fond, $contexte, $connect);
595 if (!$page) return $page;
597 if ($page['process_ins'] != 'html') {
598 // restaurer l'etat des notes
599 if (isset($page['notes']) AND $page['notes']){
600 $notes = charger_fonction("notes","inc");
601 $notes($page['notes'],'restaurer_etat');
605 xml_hack($page, true);
606 eval('?' . '>' . $page['texte']);
607 $page['texte'] = ob_get_contents();
609 $page['process_ins'] = 'html';
612 page_base_href($page['texte']);
614 // Lever un drapeau (global) si le fond utilise #SESSION
615 // a destination de public/parametrer
616 // pour remonter vers les inclusions appelantes
617 // il faut bien lever ce drapeau apres avoir evalue le fond
618 // pour ne pas faire descendre le flag vers les inclusions appelees
619 if (isset($page['invalideurs'])
620 AND isset($page['invalideurs']['session']))
621 $GLOBALS['cache_utilise_session'] = $page['invalideurs']['session'];
627 // Appeler avant et apres chaque eval()
628 // http://doc.spip.org/@xml_hack
629 function xml_hack(&$page, $echap = false) {
631 $page['texte'] = str_replace('<'.'?xml', "<\1?xml", $page['texte']);
633 $page['texte'] = str_replace("<\1?xml", '<'.'?xml', $page['texte']);
636 // http://doc.spip.org/@page_base_href
637 function page_base_href(&$texte){
638 if (!defined('_SET_HTML_BASE'))
639 // si la profondeur est superieure a 1
640 // est que ce n'est pas une url page ni une url action
641 // activer par defaut
642 define('_SET_HTML_BASE',
643 $GLOBALS['profondeur_url'] >= (_DIR_RESTREINT?
1:2)
644 AND _request(_SPIP_PAGE
) !== 'login'
645 AND !_request('action'));
648 AND isset($GLOBALS['html']) AND $GLOBALS['html']
649 AND $GLOBALS['profondeur_url']>0
650 AND ($poshead = strpos($texte,'</head>'))!==FALSE){
651 $head = substr($texte,0,$poshead);
653 if (strpos($head, '<base')===false)
656 // si aucun <base ...> n'a de href c'est bon quand meme !
658 include_spip('inc/filtres');
659 $bases = extraire_balises($head,'base');
660 foreach ($bases as $base)
661 if (extraire_attribut($base,'href'))
665 include_spip('inc/filtres_mini');
666 // ajouter un base qui reglera tous les liens relatifs
667 $base = url_absolue('./');
668 $bbase = "\n<base href=\"$base\" />";
669 if (($pos = strpos($head, '<head>')) !== false)
670 $head = substr_replace($head, $bbase, $pos+
6, 0);
671 elseif(preg_match(",<head[^>]*>,i",$head,$r)){
672 $head = str_replace($r[0], $r[0].$bbase, $head);
674 $texte = $head . substr($texte,$poshead);
676 $base = $_SERVER['REQUEST_URI'];
677 if (strpos($texte,"href='#")!==false)
678 $texte = str_replace("href='#","href='$base#",$texte);
679 if (strpos($texte, "href=\"#")!==false)
680 $texte = str_replace("href=\"#","href=\"$base#",$texte);
686 // Envoyer les entetes, en retenant ceux qui sont a usage interne
687 // et demarrent par X-Spip-...
688 // http://doc.spip.org/@envoyer_entetes
689 function envoyer_entetes($entetes) {
690 foreach ($entetes as $k => $v)
691 # if (strncmp($k, 'X-Spip-', 7))