3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2012 *
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 // flag_preserver est modifie ici, et utilise en globale
26 // use_cache sert a informer le bouton d'admin pr savoir s'il met un *
27 // contexte est utilise en globale dans le formulaire d'admin
28 global $flag_preserver, $use_cache, $contexte;
30 $contexte = calculer_contexte();
31 $page = array('contexte_implicite'=>calculer_contexte_implicite());
32 $page['contexte_implicite']['cache'] = $fond . preg_replace(',\.[a-zA-Z0-9]*$,', '', preg_replace('/[?].*$/', '', $GLOBALS['REQUEST_URI']));
33 // Cette fonction est utilisee deux fois
34 $cacher = charger_fonction('cacher', 'public', true);
35 // Les quatre derniers parametres sont modifies par la fonction:
36 // emplacement, validite, et, s'il est valide, contenu & age
38 $res = $cacher($GLOBALS['contexte'], $use_cache, $chemin_cache, $page, $lastmodified);
41 // Si un resultat est retourne, c'est un message d'impossibilite
42 if ($res) {return array('texte' => $res);}
44 if (!$chemin_cache ||
!$lastmodified) $lastmodified = time();
46 $headers_only = ($_SERVER['REQUEST_METHOD'] == 'HEAD');
47 $calculer_page = true;
49 // Pour les pages non-dynamiques (indiquees par #CACHE{duree,cache-client})
50 // une perennite valide a meme reponse qu'une requete HEAD (par defaut les
51 // pages sont dynamiques)
52 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
53 AND (!defined('_VAR_MODE') OR !_VAR_MODE
)
55 AND isset($page['entetes'])
56 AND isset($page['entetes']['Cache-Control'])
57 AND strstr($page['entetes']['Cache-Control'],'max-age=')
58 AND !strstr($_SERVER['SERVER_SOFTWARE'],'IIS/')
60 $since = preg_replace('/;.*/', '',
61 $_SERVER['HTTP_IF_MODIFIED_SINCE']);
62 $since = str_replace('GMT', '', $since);
63 if (trim($since) == gmdate("D, d M Y H:i:s", $lastmodified)) {
64 $page['status'] = 304;
66 $calculer_page = false;
70 // Si requete HEAD ou Last-modified compatible, ignorer le texte
71 // et pas de content-type (pour contrer le bouton admin de inc-public)
72 if (!$calculer_page) {
75 // si la page est prise dans le cache
77 // Informer les boutons d'admin du contexte
78 // (fourni par urls_decoder_url ci-dessous lors de la mise en cache)
79 $contexte = $page['contexte'];
81 // vider les globales url propres qui ne doivent plus etre utilisees en cas
82 // d'inversion url => objet
83 // plus necessaire si on utilise bien la fonction urls_decoder_url
84 #unset($_SERVER['REDIRECT_url_propre']);
85 #unset($_ENV['url_propre']);
88 // Compat ascendante :
89 // 1. $contexte est global
90 // (a evacuer car urls_decoder_url gere ce probleme ?)
91 // et calculer la page
92 if (!test_espace_prive()) {
93 include_spip('inc/urls');
94 list($fond,$contexte,$url_redirect) = urls_decoder_url(nettoyer_uri(),$fond,$contexte,true);
96 // squelette par defaut
100 // produire la page : peut mettre a jour $lastmodified
101 $produire_page = charger_fonction('produire_page','public');
102 $page = $produire_page($fond, $contexte, $use_cache, $chemin_cache, NULL, $page, $lastmodified, $connect);
104 $erreur = _T('info_erreur_squelette2',
105 array('fichier'=>htmlspecialchars($fond).'.'._EXTENSION_SQUELETTES
));
106 erreur_squelette($erreur);
107 // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
108 $page = array('texte' => '', 'erreur' => $erreur);
112 if ($page AND $chemin_cache) $page['cache'] = $chemin_cache;
114 auto_content_type($page);
116 $flag_preserver |
= headers_sent();
118 // Definir les entetes si ce n'est fait
119 if (!$flag_preserver) {
120 if ($GLOBALS['flag_ob']) {
121 // Si la page est vide, produire l'erreur 404 ou message d'erreur pour les inclusions
122 if (trim($page['texte']) === ''
123 AND _VAR_MODE
!= 'debug'
124 AND !isset($page['entetes']['Location']) // cette page realise une redirection, donc pas d'erreur
126 $contexte['fond_erreur'] = $fond;
127 $page = message_page_indisponible($page, $contexte);
129 // pas de cache client en mode 'observation'
130 if (defined('_VAR_MODE') AND _VAR_MODE
) {
131 $page['entetes']["Cache-Control"]= "no-cache,must-revalidate";
132 $page['entetes']["Pragma"] = "no-cache";
138 // Entete Last-Modified:
139 // eviter d'etre incoherent en envoyant un lastmodified identique
140 // a celui qu'on a refuse d'honorer plus haut (cf. #655)
142 AND !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
143 AND !isset($page['entetes']["Last-Modified"]))
144 $page['entetes']["Last-Modified"]=gmdate("D, d M Y H:i:s", $lastmodified)." GMT";
146 // fermer la connexion apres les headers si requete HEAD
148 $page['entetes']["Connection"] = "close";
154 // Contexte : lors du calcul d'une page spip etablit le contexte a partir
155 // des variables $_GET et $_POST, purgees des fausses variables var_*
156 // Note : pour hacker le contexte depuis le fichier d'appel (page.php),
157 // il est recommande de modifier $_GET['toto'] (meme si la page est
158 // appelee avec la methode POST).
160 // http://doc.spip.org/@calculer_contexte
161 function calculer_contexte() {
164 foreach($_GET as $var => $val) {
165 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
166 $contexte[$var] = $val;
168 foreach($_POST as $var => $val) {
169 if (!preg_match(_CONTEXTE_IGNORE_VARIABLES
,$var))
170 $contexte[$var] = $val;
177 * Calculer le contexte implicite, qui n'apparait pas dans le ENV d'un cache
178 * mais est utilise pour distinguer deux caches differents
180 * @staticvar string $notes
183 function calculer_contexte_implicite(){
184 static $notes = null;
186 $notes = charger_fonction('notes','inc',true);
187 $contexte_implicite = array(
188 'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ?
189 'host' => $_SERVER['HTTP_HOST'],
190 'https' => (isset($_SERVER['HTTPS']) ?
$_SERVER['HTTPS'] : ''),
191 'espace' => test_espace_prive(),
192 'marqueur' => (isset($GLOBALS['marqueur']) ?
$GLOBALS['marqueur'] : ''),
193 'marqueur_skel' => (isset($GLOBALS['marqueur_skel']) ?
$GLOBALS['marqueur_skel'] : ''),
194 'notes' => $notes?
$notes('','contexter_cache'):'',
195 'spip_version_code' => $GLOBALS['spip_version_code'],
197 return $contexte_implicite;
201 // fonction pour compatibilite arriere, probablement superflue
204 // http://doc.spip.org/@auto_content_type
205 function auto_content_type($page)
207 global $flag_preserver;
208 if (!isset($flag_preserver))
210 $flag_preserver = ($page && preg_match("/header\s*\(\s*.content\-type:/isx",$page['texte']) ||
(isset($page['entetes']['Content-Type'])));
214 // http://doc.spip.org/@inclure_page
215 function inclure_page($fond, $contexte, $connect='') {
216 static $cacher, $produire_page;
217 global $lastmodified;
219 // enlever le fond de contexte inclus car sinon il prend la main
220 // dans les sous inclusions -> boucle infinie d'inclusion identique
221 // (cette precaution n'est probablement plus utile)
222 unset($contexte['fond']);
223 $page = array('contexte_implicite'=>calculer_contexte_implicite());
224 $page['contexte_implicite']['cache'] = $fond;
225 if (is_null($cacher))
226 $cacher = charger_fonction('cacher', 'public', true);
227 // Les quatre derniers parametres sont modifies par la fonction:
228 // emplacement, validite, et, s'il est valide, contenu & age
230 $res = $cacher($contexte, $use_cache, $chemin_cache, $page, $lastinclude);
233 // $res = message d'erreur : on sort de la
234 if ($res) {return array('texte' => $res);}
236 // Si use_cache ne vaut pas 0, la page doit etre calculee
237 // produire la page : peut mettre a jour $lastinclude
238 // le contexte_cache envoye a cacher() a ete conserve et est passe a produire
240 if (is_null($produire_page))
241 $produire_page = charger_fonction('produire_page','public');
242 $page = $produire_page($fond, $contexte, $use_cache, $chemin_cache, $contexte, $page, $lastinclude, $connect);
244 // dans tous les cas, mettre a jour $lastmodified
245 $lastmodified = max($lastmodified, $lastinclude);
251 * Produire la page et la mettre en cache
252 * lorsque c'est necessaire
254 * @param string $fond
255 * @param array $contexte
256 * @param int $use_cache
257 * @param string $chemin_cache
258 * @param array $contexte_cache
260 * @param int $lastinclude
261 * @param string $connect
264 function public_produire_page_dist($fond, $contexte, $use_cache, $chemin_cache, $contexte_cache, &$page, &$lastinclude, $connect=''){
265 static $parametrer,$cacher;
267 $parametrer = charger_fonction('parametrer', 'public');
268 $page = $parametrer($fond, $contexte, $chemin_cache, $connect);
269 // et on l'enregistre sur le disque
274 AND $page['entetes']['X-Spip-Cache'] > 0){
275 if (is_null($cacher))
276 $cacher = charger_fonction('cacher', 'public', true);
277 $lastinclude = time();
279 $cacher($contexte_cache, $use_cache, $chemin_cache, $page, $lastinclude);
287 // Fonction inseree par le compilateur dans le code compile.
288 // Elle recoit un contexte pour inclure un squelette,
289 // et les valeurs du contexte de compil prepare par memoriser_contexte_compil
290 // elle-meme appelee par calculer_balise_dynamique dans references.php:
297 function inserer_balise_dynamique($contexte_exec, $contexte_compil)
299 if (!is_array($contexte_exec))
300 echo $contexte_exec; // message d'erreur etc
302 inclure_balise_dynamique($contexte_exec, true, $contexte_compil);
306 // Attention, un appel explicite a cette fonction suppose certains include
307 // $echo = faut-il faire echo ou return
309 // http://doc.spip.org/@inclure_balise_dynamique
310 function inclure_balise_dynamique($texte, $echo=true, $contexte_compil=array())
312 if (is_array($texte)) {
314 list($fond, $delainc, $contexte_inclus) = $texte;
316 // delais a l'ancienne, c'est pratiquement mort
317 $d = isset($GLOBALS['delais']) ?
$GLOBALS['delais'] : NULL;
318 $GLOBALS['delais'] = $delainc;
320 $page = recuperer_fond($fond,$contexte_inclus,array('trim'=>false, 'raw' => true, 'compil' => $contexte_compil));
322 $texte = $page['texte'];
324 $GLOBALS['delais'] = $d;
325 // Faire remonter les entetes
326 if (is_array($page['entetes'])) {
328 unset($page['entetes']['X-Spip-Cache']);
329 unset($page['entetes']['Content-Type']);
330 if (isset($GLOBALS['page']) AND is_array($GLOBALS['page'])) {
331 if (!is_array($GLOBALS['page']['entetes']))
332 $GLOBALS['page']['entetes'] = array();
333 $GLOBALS['page']['entetes'] =
334 array_merge($GLOBALS['page']['entetes'],$page['entetes']);
337 // _pipelines au pluriel array('nom_pipeline' => $args...) avec une syntaxe permettant plusieurs pipelines
338 if (isset($page['contexte']['_pipelines'])
339 AND is_array($page['contexte']['_pipelines'])
340 AND count($page['contexte']['_pipelines'])) {
341 foreach($page['contexte']['_pipelines'] as $pipe=>$args){
342 $args['contexte'] = $page['contexte'];
343 unset($args['contexte']['_pipelines']); // par precaution, meme si le risque de boucle infinie est a priori nul
356 if (defined('_VAR_MODE') AND _VAR_MODE
== 'debug') {
357 // compatibilite : avant on donnait le numero de ligne ou rien.
358 $ligne = intval(isset($contexte_compil[3]) ?
$contexte_compil[3] : $contexte_compil);
359 $GLOBALS['debug_objets']['resultat'][$ligne] = $texte;
368 // http://doc.spip.org/@message_page_indisponible
369 function message_page_indisponible ($page, $contexte) {
370 static $deja = false;
371 if ($deja) return "erreur";
373 '404' => '404 Not Found',
374 '503' => '503 Service Unavailable',
377 $contexte['status'] = ($page !== false) ?
'404' : '503';
378 $contexte['code'] = $codes[$contexte['status']];
379 $contexte['fond'] = '404'; // gere les 2 erreurs
380 if (!isset($contexte['lang']))
381 $contexte['lang'] = $GLOBALS['spip_lang'];
384 // passer aux plugins qui peuvent decider d'une page d'erreur plus pertinent
385 // ex restriction d'acces => 401
386 $contexte = pipeline('page_indisponible',$contexte);
388 // produire la page d'erreur
389 $page = inclure_page($contexte['fond'], $contexte);
391 $page = inclure_page('404', $contexte);
392 $page['status'] = $contexte['status'];
397 // temporairement ici : a mettre dans le futur inc/modeles
398 // creer_contexte_de_modele('left', 'autostart=true', ...) renvoie un array()
399 // http://doc.spip.org/@creer_contexte_de_modele
400 function creer_contexte_de_modele($args) {
402 foreach ($args as $var=>$val) {
403 if (is_int($var)){ // argument pas formate
404 if (in_array($val, array('left', 'right', 'center'))) {
406 $contexte[$var] = $val;
408 $args = explode('=', $val);
409 if (count($args)>=2) // Flashvars=arg1=machin&arg2=truc genere plus de deux args
410 $contexte[trim($args[0])] = substr($val,strlen($args[0])+
1);
411 else // notation abregee
412 $contexte[trim($val)] = trim($val);
416 $contexte[$var] = $val;
422 // Calcule le modele et retourne la mini-page ainsi calculee
423 // http://doc.spip.org/@inclure_modele
424 function inclure_modele($type, $id, $params, $lien, $connect='', $env=array()) {
427 if (++
$compteur>10) return ''; # ne pas boucler indefiniment
429 $type = strtolower($type);
433 $params = array_filter(explode('|', $params));
435 list(,$soustype) = each($params);
436 $soustype = strtolower($soustype);
437 if (in_array($soustype,
438 array('left', 'right', 'center', 'ajax'))) {
439 list(,$soustype) = each($params);
440 $soustype = strtolower($soustype);
443 if (preg_match(',^[a-z0-9_]+$,', $soustype)) {
444 if (!trouve_modele($fond = ($type.'_'.$soustype))) {
448 // enlever le sous type des params
449 $params = array_diff($params,array($soustype));
453 // Si ca marche pas en precisant le sous-type, prendre le type
454 if (!$fond AND !trouve_modele($fond = $type)){
455 spip_log("Modele $type introuvable",_LOG_INFO_IMPORTANTE
);
458 $fond = 'modeles/'.$fond;
461 $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
463 // Le numero du modele est mis dans l'environnement
464 // d'une part sous l'identifiant "id"
465 // et d'autre part sous l'identifiant de la cle primaire
466 // par la fonction id_table_objet,
467 // (<article1> =>> article =>> id_article =>> id_article=1)
468 $_id = id_table_objet($type);
469 $contexte['id'] = $contexte[$_id] = $id;
472 $contexte['class'] = $class;
474 // Si un lien a ete passe en parametre, ex: [<modele1>->url]
476 # un eventuel guillemet (") sera reechappe par #ENV
477 $contexte['lien'] = str_replace(""",'"', $lien['href']);
478 $contexte['lien_class'] = $lien['class'];
479 $contexte['lien_mime'] = $lien['mime'];
482 // Traiter les parametres
483 // par exemple : <img1|center>, <emb12|autostart=true> ou <doc1|lang=en>
484 $arg_list = creer_contexte_de_modele($params);
485 $contexte['args'] = $arg_list; // on passe la liste des arguments du modeles dans une variable args
486 $contexte = array_merge($contexte,$arg_list);
489 // Appliquer le modele avec le contexte
490 $retour = recuperer_fond($fond, $contexte, array(), $connect);
493 // Regarder si le modele tient compte des liens (il *doit* alors indiquer
494 // spip_lien_ok dans les classes de son conteneur de premier niveau ;
495 // sinon, s'il y a un lien, on l'ajoute classiquement
496 if (strstr(' ' . ($classes = extraire_attribut($retour, 'class')).' ',
498 $retour = inserer_attribut($retour, 'class',
499 trim(str_replace(' spip_lien_ok ', ' ', " $classes ")));
501 $retour = "<a href='".$lien['href']."' class='".$lien['class']."'>".$retour."</a>";
505 return (isset($arg_list['ajax'])AND $arg_list['ajax']=='ajax')
506 ?
encoder_contexte_ajax($contexte,'',$retour)
510 // Un inclure_page qui marche aussi pour l'espace prive
511 // fonction interne a spip, ne pas appeler directement
512 // pour recuperer $page complet, utiliser:
513 // recuperer_fond($fond,$contexte,array('raw'=>true))
514 // http://doc.spip.org/@evaluer_fond
515 function evaluer_fond ($fond, $contexte=array(), $connect=null) {
517 $page = inclure_page($fond, $contexte, $connect);
519 if (!$page) return $page;
520 // eval $page et affecte $res
521 include _ROOT_RESTREINT
."public/evaluer_page.php";
523 // Lever un drapeau (global) si le fond utilise #SESSION
524 // a destination de public/parametrer
525 // pour remonter vers les inclusions appelantes
526 // il faut bien lever ce drapeau apres avoir evalue le fond
527 // pour ne pas faire descendre le flag vers les inclusions appelees
528 if (isset($page['invalideurs'])
529 AND isset($page['invalideurs']['session']))
530 $GLOBALS['cache_utilise_session'] = $page['invalideurs']['session'];
536 // http://doc.spip.org/@page_base_href
537 function page_base_href(&$texte){
538 static $set_html_base = null;
539 if (is_null($set_html_base)){
540 if (!defined('_SET_HTML_BASE'))
541 // si la profondeur est superieure a 1
542 // est que ce n'est pas une url page ni une url action
543 // activer par defaut
545 $GLOBALS['profondeur_url'] >= (_DIR_RESTREINT?
1:2)
546 AND _request(_SPIP_PAGE
) !== 'login'
547 AND !_request('action'))?
true:false);
549 $set_html_base = _SET_HTML_BASE
;
553 AND isset($GLOBALS['html']) AND $GLOBALS['html']
554 AND $GLOBALS['profondeur_url']>0
555 AND ($poshead = strpos($texte,'</head>'))!==FALSE){
556 $head = substr($texte,0,$poshead);
558 if (strpos($head, '<base')===false)
561 // si aucun <base ...> n'a de href c'est bon quand meme !
563 include_spip('inc/filtres');
564 $bases = extraire_balises($head,'base');
565 foreach ($bases as $base)
566 if (extraire_attribut($base,'href'))
570 include_spip('inc/filtres_mini');
571 // ajouter un base qui reglera tous les liens relatifs
572 $base = url_absolue('./');
573 $bbase = "\n<base href=\"$base\" />";
574 if (($pos = strpos($head, '<head>')) !== false)
575 $head = substr_replace($head, $bbase, $pos+
6, 0);
576 elseif(preg_match(",<head[^>]*>,i",$head,$r)){
577 $head = str_replace($r[0], $r[0].$bbase, $head);
579 $texte = $head . substr($texte,$poshead);
581 $base = $_SERVER['REQUEST_URI'];
582 if (strpos($texte,"href='#")!==false)
583 $texte = str_replace("href='#","href='$base#",$texte);
584 if (strpos($texte, "href=\"#")!==false)
585 $texte = str_replace("href=\"#","href=\"$base#",$texte);
591 // Envoyer les entetes, en retenant ceux qui sont a usage interne
592 // et demarrent par X-Spip-...
593 // http://doc.spip.org/@envoyer_entetes
594 function envoyer_entetes($entetes) {
595 foreach ($entetes as $k => $v)
596 # if (strncmp($k, 'X-Spip-', 7))
597 @header
(strlen($v)?
"$k: $v":$k);