3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2019 *
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 \***************************************************************************/
14 * Utilitaires indispensables autour du serveur Http.
16 * @package SPIP\Core\Utilitaires
19 if (!defined('_ECRIRE_INC_VERSION')) {
25 * Cherche une fonction surchargeable et en retourne le nom exact,
26 * après avoir chargé le fichier la contenant si nécessaire.
28 * Charge un fichier (suivant les chemins connus) et retourne si elle existe
29 * le nom de la fonction homonyme `$dir_$nom`, ou suffixé `$dir_$nom_dist`
31 * Peut être appelé plusieurs fois, donc optimisé.
34 * @uses include_spip() Pour charger le fichier
37 * $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
38 * $envoyer_mail($email, $sujet, $texte);
42 * Nom de la fonction (et du fichier)
43 * @param string $dossier
44 * Nom du dossier conteneur
45 * @param bool $continue
46 * true pour ne pas râler si la fonction n'est pas trouvée
48 * Nom de la fonction, ou false.
50 function charger_fonction($nom, $dossier = 'exec', $continue = false) {
51 static $echecs = array();
53 if (strlen($dossier) and substr($dossier, -1) != '/') {
56 $f = str_replace('/', '_', $dossier) . $nom;
58 if (function_exists($f)) {
61 if (function_exists($g = $f . '_dist')) {
65 if (isset($echecs[$f])) {
68 // Sinon charger le fichier de declaration si plausible
70 if (!preg_match(',^\w+$,', $f)) {
73 } //appel interne, on passe
74 include_spip('inc/minipres');
79 // passer en minuscules (cf les balises de formulaires)
80 // et inclure le fichier
81 if (!$inc = include_spip($dossier . ($d = strtolower($nom)))
82 // si le fichier truc/machin/nom.php n'existe pas,
83 // la fonction peut etre definie dans truc/machin.php qui regroupe plusieurs petites fonctions
84 and strlen(dirname($dossier)) and dirname($dossier) != '.'
86 include_spip(substr($dossier, 0, -1));
88 if (function_exists($f)) {
91 if (function_exists($g)) {
96 return $echecs[$f] = false;
99 // Echec : message d'erreur
100 spip_log("fonction $nom ($f ou $g) indisponible" .
101 ($inc ?
"" : " (fichier $d absent de $dossier)"));
103 include_spip('inc/minipres');
104 echo minipres(_T('forum_titre_erreur'),
105 _T('fichier_introuvable', array('fichier' => '<b>' . spip_htmlentities($d) . '</b>')),
106 array('all_inline'=>true,'status'=>404));
111 * Inclusion unique avec verification d'existence du fichier + log en crash sinon
113 * @param string $file
116 function include_once_check($file) {
117 if (file_exists($file)) {
122 $crash = (isset($GLOBALS['meta']['message_crash_plugins']) ?
unserialize($GLOBALS['meta']['message_crash_plugins']) : '');
123 $crash = ($crash ?
$crash : array());
124 $crash[$file] = true;
125 ecrire_meta('message_crash_plugins', serialize($crash));
132 * Inclut un fichier PHP (en le cherchant dans les chemins)
135 * @uses find_in_path()
138 * include_spip('inc/texte');
142 * Nom du fichier (sans l'extension)
143 * @param bool $include
144 * - true pour inclure le fichier,
145 * - false ne fait que le chercher
146 * @return string|bool
147 * - false : fichier introuvable
148 * - string : chemin du fichier trouvé
150 function include_spip($f, $include = true) {
151 return find_in_path($f . '.php', '', $include);
155 * Requiert un fichier PHP (en le cherchant dans les chemins)
157 * @uses find_in_path()
158 * @see include_spip()
161 * require_spip('inc/texte');
165 * Nom du fichier (sans l'extension)
166 * @return string|bool
167 * - false : fichier introuvable
168 * - string : chemin du fichier trouvé
170 function require_spip($f) {
171 return find_in_path($f . '.php', '', 'required');
175 * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
177 * Un pipeline est lie a une action et une valeur
178 * chaque element du pipeline est autorise a modifier la valeur
179 * le pipeline execute les elements disponibles pour cette action,
180 * les uns apres les autres, et retourne la valeur finale
182 * Cf. compose_filtres dans references.php, qui est la
183 * version compilee de cette fonctionnalite
184 * appel unitaire d'une fonction du pipeline
185 * utilisee dans le script pipeline precompile
187 * on passe $val par reference pour limiter les allocations memoire
189 * @param string $fonc
190 * Nom de la fonction appelée par le pipeline
191 * @param string|array $val
192 * Les paramètres du pipeline, son environnement
193 * @return string|array $val
194 * Les paramètres du pipeline modifiés
196 function minipipe($fonc, &$val) {
198 if (function_exists($fonc)) {
199 $val = call_user_func($fonc, $val);
202 if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
203 and $methode = array($regs[1], $regs[2])
204 and is_callable($methode)
206 $val = call_user_func($methode, $val);
208 spip_log("Erreur - '$fonc' non definie !");
216 * Appel d’un pipeline
218 * Exécute le pipeline souhaité, éventuellement avec des données initiales.
219 * Chaque plugin qui a demandé à voir ce pipeline vera sa fonction spécifique appelée.
220 * Les fonctions (des plugins) appelées peuvent modifier à leur guise le contenu.
222 * Deux types de retours. Si `$val` est un tableau de 2 éléments, avec une clé `data`
223 * on retourne uniquement ce contenu (`$val['data']`) sinon on retourne tout `$val`.
227 * Appel du pipeline `pre_insertion`
229 * $champs = pipeline('pre_insertion', array(
230 * 'args' => array('table' => 'spip_articles'),
235 * @param string $action
237 * @param null|string|array $val
238 * Données à l’entrée du pipeline
242 function pipeline($action, $val = null) {
245 // chargement initial des fonctions mises en cache, ou generation du cache
247 if (!($ok = @is_readable
($charger = _CACHE_PIPELINES
))) {
248 include_spip('inc/plugin');
249 // generer les fichiers php precompiles
250 // de chargement des plugins et des pipelines
251 actualise_plugins_actifs();
252 if (!($ok = @is_readable
($charger))) {
253 spip_log("fichier $charger pas cree");
258 include_once $charger;
262 // appliquer notre fonction si elle existe
263 $fonc = 'execute_pipeline_' . strtolower($action);
264 if (function_exists($fonc)) {
268 spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR
);
271 // si le flux est une table avec 2 cle args&data
272 // on ne ressort du pipe que les donnees dans 'data'
273 // array_key_exists pour php 4.1.0
276 and (array_key_exists('data', $val))
285 * Enregistrement des événements
287 * Signature : `spip_log(message[,niveau|type|type.niveau])`
289 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
291 * Les différents niveaux possibles sont :
293 * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
294 * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
295 * - `_LOG_CRITIQUE` : 'CRITIQUE'
296 * - `_LOG_ERREUR` : 'ERREUR'
297 * - `_LOG_AVERTISSEMENT` : 'WARNING'
298 * - `_LOG_INFO_IMPORTANTE` : '!INFO'
299 * - `_LOG_INFO` : 'info'
300 * - `_LOG_DEBUG` : 'debug'
305 * spip_log($message, 'recherche')
306 * spip_log($message, _LOG_DEBUG)
307 * spip_log($message, 'recherche.'._LOG_DEBUG)
311 * @link http://programmer.spip.net/spip_log
312 * @uses inc_log_dist()
314 * @param string $message
316 * @param string|int $name
318 * - int indique le niveau de log, tel que `_LOG_DEBUG`
319 * - string indique le type de log
320 * - `string.int` indique les 2 éléments.
321 * Cette dernière notation est controversée mais le 3ème
322 * paramètre est planté pour cause de compatibilité ascendante.
324 function spip_log($message = null, $name = null) {
325 static $pre = array();
327 preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
328 if (!isset($regs[1]) or !$logname = $regs[1]) {
331 if (!isset($regs[2])) {
335 $niveau = intval($regs[2]);
338 if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE
: _LOG_INFO_IMPORTANTE
)) {
342 _LOG_ALERTE_ROUGE
=> 'ALERTE:',
343 _LOG_CRITIQUE
=> 'CRITIQUE:',
344 _LOG_ERREUR
=> 'ERREUR:',
345 _LOG_AVERTISSEMENT
=> 'WARNING:',
346 _LOG_INFO_IMPORTANTE
=> '!INFO:',
347 _LOG_INFO
=> 'info:',
348 _LOG_DEBUG
=> 'debug:'
350 $log = charger_fonction('log', 'inc');
352 if (!is_string($message)) {
353 $message = print_r($message, true);
355 $log($pre[$niveau] . ' ' . $message, $logname);
360 * Enregistrement des journaux
362 * @uses inc_journal_dist()
363 * @param string $phrase Texte du journal
364 * @param array $opt Tableau d'options
366 function journal($phrase, $opt = array()) {
367 $journal = charger_fonction('journal', 'inc');
368 $journal($phrase, $opt);
373 * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
374 * ou pioché dans un tableau transmis
379 * @param bool|array $c
380 * Tableau transmis (sinon cherche dans GET ou POST)
382 * - null si la clé n'a pas été trouvée
383 * - la valeur de la clé sinon.
385 function _request($var, $c = false) {
388 return isset($c[$var]) ?
$c[$var] : null;
391 if (isset($_GET[$var])) {
393 } elseif (isset($_POST[$var])) {
399 // Si on est en ajax et en POST tout a ete encode
400 // via encodeURIComponent, il faut donc repasser
401 // dans le charset local...
404 and isset($GLOBALS['meta']['charset'])
405 and $GLOBALS['meta']['charset'] != 'utf-8'
407 // check rapide mais pas fiable
408 and preg_match(',[\x80-\xFF],', $a)
410 and include_spip('inc/charsets')
413 return importer_charset($a, 'utf-8');
421 * Affecte une valeur à une clé (pour usage avec `_request()`)
423 * @see _request() Pour obtenir la valeur
424 * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
426 * @param string $var Nom de la clé
427 * @param string $val Valeur à affecter
428 * @param bool|array $c Tableu de données (sinon utilise `$_GET` et `$_POST`)
430 * - array $c complété si un $c est transmis,
433 function set_request($var, $val = null, $c = false) {
449 return false; # n'affecte pas $c
453 * Sanitizer une valeur *SI* elle provient du GET ou POST
454 * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
455 * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
456 * mais qui doit etre nettoyee si elle provient de l'URL
459 * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
460 * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
461 * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
463 * @param string|array $value
464 * @param string|array $key
465 * @param string $sanitize_function
466 * @return array|mixed|string
468 function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
469 if (is_array($value)) {
471 $key = array_keys($value);
473 if (!is_array($key)) {
476 foreach ($key as $k) {
477 if (!empty($value[$k])) {
478 $value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
483 // si la valeur vient des GET ou POST on la sanitize
484 if (!empty($value) and $value == _request($key)) {
485 $value = $sanitize_function($value);
491 * Tester si une URL est absolue
493 * On est sur le web, on exclut certains protocoles,
494 * notamment 'file://', 'php://' et d'autres…
499 function tester_url_absolue($url) {
501 if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
504 and $p = strtolower(rtrim($m[1], ':'))
505 and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
515 * Prend une URL et lui ajoute/retire un paramètre
518 * @link http://www.spip.net/4255
521 * [(#SELF|parametre_url{suite,18})] (ajout)
522 * [(#SELF|parametre_url{suite,''})] (supprime)
523 * [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
526 * @param string $url URL
527 * @param string $c Nom du paramètre
528 * @param string|array|null $v Valeur du paramètre
529 * @param string $sep Séparateur entre les paramètres
532 function parametre_url($url, $c, $v = null, $sep = '&') {
533 // requete erronnee : plusieurs variable dans $c et aucun $v
534 if (strpos($c, "|") !== false and is_null($v)) {
539 if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
547 $url = preg_split(',[?]|&|&,', $url);
550 $a = array_shift($url);
555 $regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
556 $ajouts = array_flip(explode('|', $c));
557 $u = is_array($v) ?
$v : rawurlencode($v);
558 $testv = (is_array($v) ?
count($v) : strlen($v));
560 // lire les variables et agir
561 foreach ($url as $n => $val) {
562 if (preg_match($regexp, urldecode($val), $r)) {
563 $r = array_pad($r, 3, null);
565 // c'est un tableau, on memorise les valeurs
566 if (substr($r[1], -2) == "[]") {
570 $v_read[] = $r[2] ?
substr($r[2], 1) : '';
571 } // c'est un scalaire, on retourne direct
573 return $r[2] ?
substr($r[2], 1) : '';
579 // Ajout. Pour une variable, remplacer au meme endroit,
580 // pour un tableau ce sera fait dans la prochaine boucle
581 elseif (substr($r[1], -2) != '[]') {
582 $url[$n] = $r[1] . '=' . $u;
583 unset($ajouts[$r[1]]);
585 // Pour les tableaux on laisse tomber les valeurs de
586 // départ, on remplira à l'étape suivante
593 // traiter les parametres pas encore trouves
595 and $args = func_get_args()
596 and count($args) == 2
598 return $v_read; // rien trouve ou un tableau
600 foreach ($ajouts as $k => $n) {
602 $url[] = $k . '=' . $u;
604 $id = (substr($k, -2) == '[]') ?
$k : ($k . "[]");
606 $url[] = $id . '=' . (is_array($w) ?
'Array' : $w);
612 // eliminer les vides
613 $url = array_filter($url);
615 // recomposer l'adresse
617 $a .= '?' . join($sep, $url);
624 * Ajoute (ou retire) une ancre sur une URL
626 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
627 * et on remplace ceux à l'interieur ou au bout par `-`
630 * - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
631 * - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
632 * @uses translitteration()
634 * @param string $ancre
637 function ancre_url($url, $ancre) {
639 if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
642 if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
643 if (!function_exists('translitteration')) {
644 include_spip('inc/charsets');
646 $ancre = preg_replace(
647 array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
649 translitteration($ancre)
652 return $url . (strlen($ancre) ?
'#' . $ancre : '');
656 * Pour le nom du cache, les `types_urls` et `self`
658 * @param string|null $reset
661 function nettoyer_uri($reset = null) {
662 static $done = false;
664 if (!is_null($reset)) {
665 return $propre = $reset;
671 return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
675 * Nettoie une request_uri des paramètres var_xxx
676 * @param $request_uri
679 function nettoyer_uri_var($request_uri) {
680 $uri1 = $request_uri;
683 $uri1 = preg_replace(',([?&])(PHPSESSID|(var_[^=&]*))=[^&]*(&|$),i',
685 } while ($uri <> $uri1);
686 return preg_replace(',[?&]$,', '', $uri1);
691 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
694 * Style des esperluettes
699 function self($amp = '&', $root = false) {
700 $url = nettoyer_uri();
703 // si pas de profondeur on peut tronquer
704 $GLOBALS['profondeur_url'] < (_DIR_RESTREINT ?
1 : 2)
705 // sinon c'est OK si _SET_HTML_BASE a ete force a false
706 or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE
))
708 $url = preg_replace(',^[^?]*/,', '', $url);
710 // ajouter le cas echeant les variables _POST['id_...']
711 foreach ($_POST as $v => $c) {
712 if (substr($v, 0, 3) == 'id_') {
713 $url = parametre_url($url, $v, $c, '&');
717 // supprimer les variables sans interet
718 if (test_espace_prive()) {
719 $url = preg_replace(',([?&])('
721 . 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
722 $url = preg_replace(',([?&])[&]+,', '\1', $url);
723 $url = preg_replace(',[&]$,', '\1', $url);
727 include_spip('inc/filtres_mini');
728 $url = spip_htmlspecialchars($url);
730 $url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
733 if ($amp != '&') {
734 $url = str_replace('&', $amp, $url);
737 // Si ca demarre par ? ou vide, donner './'
738 $url = preg_replace(',^([?].*)?$,', './\1', $url);
745 * Indique si on est dans l'espace prive
748 * true si c'est le cas, false sinon.
750 function test_espace_prive() {
751 return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE
: false;
755 * Vérifie la présence d'un plugin actif, identifié par son préfixe
757 * @param string $plugin
760 function test_plugin_actif($plugin) {
761 return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ?
true : false;
765 * Traduction des textes de SPIP
767 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
770 * @uses inc_traduire_dist()
774 * _T('bouton_enregistrer')
775 * _T('medias:image_tourner_droite')
776 * _T('medias:erreurs', array('nb'=>3))
777 * _T("email_sujet", array('spip_lang'=>$lang_usager))
780 * @param string $texte
783 * Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
784 * @param array $options
785 * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
786 * - bool force : forcer un retour meme si la chaine n'a pas de traduction
787 * - bool sanitize : nettoyer le html suspect dans les arguments
791 function _T($texte, $args = array(), $options = array()) {
792 static $traduire = false;
793 $o = array('class' => '', 'force' => true, 'sanitize' => true);
795 // support de l'ancien argument $class
796 if (is_string($options)) {
797 $options = array('class' => $options);
799 $o = array_merge($o, $options);
803 $traduire = charger_fonction('traduire', 'inc');
804 include_spip('inc/lang');
807 // On peut passer explicitement la langue dans le tableau
808 // On utilise le même nom de variable que la globale
809 if (isset($args['spip_lang'])) {
810 $lang = $args['spip_lang'];
811 // On l'enleve pour ne pas le passer au remplacement
812 unset($args['spip_lang']);
813 } // Sinon on prend la langue du contexte
815 $lang = $GLOBALS['spip_lang'];
817 $text = $traduire($texte, $lang);
819 if (!strlen($text)) {
826 // pour les chaines non traduites, assurer un service minimum
827 if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
828 $text = str_replace('_', ' ',
829 (($n = strpos($text, ':')) === false ?
$texte :
830 substr($texte, $n +
1)));
836 return _L($text, $args, $o);
842 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
844 * Cette fonction est également appelée dans le code source de SPIP quand une
845 * chaîne n'est pas encore dans les fichiers de langue.
850 * _L('Texte avec @nb@ ...', array('nb'=>3)
853 * @param string $text
856 * Couples (variable => valeur) à transformer dans le texte
857 * @param array $options
858 * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
859 * - bool sanitize : nettoyer le html suspect dans les arguments
863 function _L($text, $args = array(), $options = array()) {
865 $defaut_options = array(
869 // support de l'ancien argument $class
870 if ($options and is_string($options)) {
871 $options = array('class' => $options);
873 if (is_array($options)) {
874 $options +
= $defaut_options;
876 $options = $defaut_options;
879 if (is_array($args) and count($args)) {
880 if (!function_exists('interdire_scripts')) {
881 include_spip('inc/texte');
883 if (!function_exists('echapper_html_suspect')) {
884 include_spip('inc/texte_mini');
886 foreach ($args as $name => $value) {
887 if ($options['sanitize']) {
888 $value = echapper_html_suspect($value);
889 $value = interdire_scripts($value, -1);
891 if (!empty($options['class'])) {
892 $value = "<span class='".$options['class']."'>$value</span>";
894 $t = str_replace("@$name@", $value, $text);
900 // Si des variables n'ont pas ete inserees, le signaler
901 // (chaines de langues pas a jour)
903 spip_log("$f: variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG
);
907 if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
908 return "<span class='debug-traduction-erreur'>$text</span>";
916 * Retourne un joli chemin de répertoire
918 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
919 * ou `tmp/` au lieu de `../tmp/`
921 * @param stirng $rep Chemin d’un répertoire
924 function joli_repertoire($rep) {
925 $a = substr($rep, 0, 1);
926 if ($a <> '.' and $a <> '/') {
927 $rep = (_DIR_RESTREINT ?
'' : _DIR_RESTREINT_ABS
) . $rep;
929 $rep = preg_replace(',(^\.\.\/),', '', $rep);
936 * Débute ou arrête un chronomètre et retourne sa valeur
938 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
939 * la seconde fois pour l’arrêter et récupérer la valeur
943 * spip_timer('papoter');
945 * $duree = spip_timer('papoter');
951 * - false : retour en texte humainement lisible
952 * - true : retour en millisecondes
953 * @return float|int|string|void
955 function spip_timer($t = 'rien', $raw = false) {
959 // microtime peut contenir les microsecondes et le temps
960 $b = explode(' ', $b);
961 if (count($b) == 2) {
965 if (!isset($time[$t])) {
968 $p = ($a +
$b - $time[$t]) * 1000;
977 $s = sprintf("%d ", $x = floor($p / 1000));
981 return $s . sprintf($s ?
"%07.3f ms" : "%.3f ms", $p);
986 // Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
987 // sinon renvoie True et le date sauf si ca n'est pas souhaite
988 // http://code.spip.net/@spip_touch
989 function spip_touch($fichier, $duree = 0, $touch = true) {
992 if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
996 if ($touch !== false) {
997 if (!@touch
($fichier)) {
998 spip_unlink($fichier);
1001 @chmod
($fichier, _SPIP_CHMOD
& ~
0111);
1009 * Action qui déclenche une tache de fond
1011 * @see queue_affichage_cron()
1012 * @see action_super_cron_dist()
1015 function action_cron() {
1016 include_spip('inc/headers');
1017 http_status(204); // No Content
1018 header("Connection: close");
1019 define('_DIRECT_CRON_FORCE', true);
1024 * Exécution des tâches de fond
1026 * @uses inc_genie_dist()
1028 * @param array $taches
1030 * @param array $taches_old
1031 * Tâches forcées, pour compat avec ancienne syntaxe
1033 * True si la tache a pu être effectuée
1035 function cron($taches = array(), $taches_old = array()) {
1036 // si pas en mode cron force, laisser tomber.
1037 if (!defined('_DIRECT_CRON_FORCE')) {
1040 if (!is_array($taches)) {
1041 $taches = $taches_old;
1042 } // compat anciens appels
1043 // si taches a inserer en base et base inaccessible, laisser tomber
1044 // sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1045 // queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1046 // et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1047 if ($taches and count($taches) and !spip_connect()) {
1050 spip_log("cron !", 'jq' . _LOG_DEBUG
);
1051 if ($genie = charger_fonction('genie', 'inc', true)) {
1052 return $genie($taches);
1059 * Ajout d'une tache dans la file d'attente
1061 * @param string $function
1062 * Le nom de la fonction PHP qui doit être appelée.
1063 * @param string $description
1064 * Une description humainement compréhensible de ce que fait la tâche
1065 * (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1066 * @param array $arguments
1067 * Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1068 * @param string $file
1069 * Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1070 * exemple : `'inc/mail'` : il ne faut pas indiquer .php
1071 * Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1072 * @param bool $no_duplicate
1073 * Facultatif, `false` par défaut
1075 * - si `true` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction et les mêmes arguments.
1076 * - si `function_only` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction indépendamment de ses arguments
1078 * Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1079 * Si `0` ou une date passée, la tâche sera exécutée aussitôt que possible (en général en fin hit, en asynchrone).
1080 * @param int $priority
1081 * Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1082 * Les tâches sont exécutées par ordre de priorité décroissante, une fois leur date d’exécution passée. La priorité est surtout utilisée quand une tâche cron indique qu’elle n’a pas fini et doit être relancée : dans ce cas SPIP réduit sa priorité pour être sûr que celle tâche ne monopolise pas la file d’attente.
1084 * Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1086 function job_queue_add(
1089 $arguments = array(),
1091 $no_duplicate = false,
1095 include_spip('inc/queue');
1097 return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1101 * Supprimer une tache de la file d'attente
1103 * @param int $id_job
1104 * id of jonb to delete
1107 function job_queue_remove($id_job) {
1108 include_spip('inc/queue');
1110 return queue_remove_job($id_job);
1114 * Associer une tache a un/des objets de SPIP
1116 * @param int $id_job
1118 * @param array $objets
1119 * can be a simple array('objet'=>'article', 'id_objet'=>23)
1120 * or an array of simple array to link multiples objet in one time
1122 function job_queue_link($id_job, $objets) {
1123 include_spip('inc/queue');
1125 return queue_link_job($id_job, $objets);
1130 * Renvoyer le temps de repos restant jusqu'au prochain job
1132 * @staticvar int $queue_next_job_time
1133 * @see queue_set_next_job_time()
1134 * @param int|bool $force
1135 * Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1137 * - si `true`, force la relecture depuis le fichier
1138 * - si int, affecte la static directement avec la valeur
1141 * - `0` si un job est à traiter
1142 * - `null` si la queue n'est pas encore initialisée
1144 function queue_sleep_time_to_next_job($force = null) {
1145 static $queue_next_job_time = -1;
1146 if ($force === true) {
1147 $queue_next_job_time = -1;
1149 $queue_next_job_time = $force;
1152 if ($queue_next_job_time == -1) {
1153 if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1154 define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP
. "job_queue_next.txt");
1156 // utiliser un cache memoire si dispo
1157 if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY
) {
1158 $queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME
);
1160 $queue_next_job_time = null;
1161 if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME
, $contenu)) {
1162 $queue_next_job_time = intval($contenu);
1167 if (is_null($queue_next_job_time)) {
1170 if (!$_SERVER['REQUEST_TIME']) {
1171 $_SERVER['REQUEST_TIME'] = time();
1174 return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1179 * Transformation XML des `&` en `&`
1181 * @pipeline post_typo
1185 function quote_amp($u) {
1186 return preg_replace(
1187 "/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1193 * Produit une balise `<script>` valide
1197 * echo http_script('alert("ok");');
1198 * echo http_script('','js/jquery.js');
1201 * @param string $script
1202 * Code source du script
1203 * @param string $src
1204 * Permet de faire appel à un fichier javascript distant
1205 * @param string $noscript
1206 * Contenu de la balise `<noscript>`
1208 * Balise HTML `<script>` et son contenu
1210 function http_script($script, $src = '', $noscript = '') {
1211 static $done = array();
1213 if ($src && !isset($done[$src])) {
1215 $src = find_in_path($src, _JAVASCRIPT
);
1216 $src = " src='$src'";
1221 $script = ("/*<![CDATA[*/\n" .
1222 preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1226 $noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1229 return ($src or $script or $noscript)
1230 ?
"<script type='text/javascript'$src>$script</script>$noscript"
1236 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1238 * Transforme n'importe quel texte en une chaîne utilisable
1239 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1240 * simples (`'` uniquement ; pas `"`)
1242 * Utile particulièrement en filtre dans un squelettes
1243 * pour écrire un contenu dans une variable JS ou PHP.
1245 * Échappe les apostrophes (') du contenu transmis.
1247 * @link http://www.spip.net/4281
1249 * PHP dans un squelette
1251 * $x = '[(#TEXTE|texte_script)]';
1254 * JS dans un squelette (transmettre une chaîne de langue)
1256 * $x = '<:afficher_calendrier|texte_script:>';
1260 * @param string $texte
1265 function texte_script($texte) {
1266 return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1271 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1273 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1274 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1275 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1277 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1278 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1279 * `$dossier_squelette` si définie qui resteront devant)
1281 * Retourne dans tous les cas la liste des chemins.
1284 * Cette fonction est appelée à plusieurs endroits et crée une liste
1285 * de chemins finale à peu près de la sorte :
1287 * - dossiers squelettes (si globale précisée)
1289 * - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1290 * d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1292 * - squelettes-dist/
1296 * @param string $dir_path
1297 * - Répertoire(s) à empiler au path
1298 * - '' provoque un recalcul des chemins.
1300 * Liste des chemins, par ordre de priorité.
1302 function _chemin($dir_path = null) {
1303 static $path_base = null;
1304 static $path_full = null;
1305 if ($path_base == null) {
1306 // Chemin standard depuis l'espace public
1307 $path = defined('_SPIP_PATH') ? _SPIP_PATH
:
1309 _DIR_RACINE
. 'squelettes-dist/:' .
1310 _DIR_RACINE
. 'prive/:' .
1312 // Ajouter squelettes/
1313 if (@is_dir
(_DIR_RACINE
. 'squelettes')) {
1314 $path = _DIR_RACINE
. 'squelettes/:' . $path;
1316 foreach (explode(':', $path) as $dir) {
1317 if (strlen($dir) and substr($dir, -1) != '/') {
1320 $path_base[] = $dir;
1322 $path_full = $path_base;
1323 // Et le(s) dossier(s) des squelettes nommes
1324 if (strlen($GLOBALS['dossier_squelettes'])) {
1325 foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1326 array_unshift($path_full, ($d[0] == '/' ?
'' : _DIR_RACINE
) . $d . '/');
1329 $GLOBALS['path_sig'] = md5(serialize($path_full));
1331 if ($dir_path === null) {
1335 if (strlen($dir_path)) {
1337 if (reset($path_base) == _DIR_RACINE
. 'squelettes/') {
1338 $tete = array_shift($path_base);
1340 $dirs = array_reverse(explode(':', $dir_path));
1341 foreach ($dirs as $dir_path) {
1342 #if ($dir_path{0}!='/')
1343 # $dir_path = $dir_path;
1344 if (substr($dir_path, -1) != '/') {
1347 if (!in_array($dir_path, $path_base)) {
1348 array_unshift($path_base, $dir_path);
1351 if (strlen($tete)) {
1352 array_unshift($path_base, $tete);
1355 $path_full = $path_base;
1356 // Et le(s) dossier(s) des squelettes nommes
1357 if (strlen($GLOBALS['dossier_squelettes'])) {
1358 foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1359 array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ?
'' : _DIR_RACINE
) . $d . '/');
1363 $GLOBALS['path_sig'] = md5(serialize($path_full));
1369 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1371 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1375 * @return array Liste de chemins
1377 function creer_chemin() {
1378 $path_a = _chemin();
1381 // on calcule le chemin si le dossier skel a change
1382 if ($c != $GLOBALS['dossier_squelettes']) {
1383 // assurer le non plantage lors de la montee de version :
1384 $c = $GLOBALS['dossier_squelettes'];
1385 $path_a = _chemin(''); // forcer un recalcul du chemin
1392 function lister_themes_prives() {
1393 static $themes = null;
1394 if (is_null($themes)) {
1395 // si pas encore definie
1396 if (!defined('_SPIP_THEME_PRIVE')) {
1397 define('_SPIP_THEME_PRIVE', 'spip');
1399 $themes = array(_SPIP_THEME_PRIVE
);
1400 // lors d'une installation neuve, prefs n'est pas definie.
1401 if (isset($GLOBALS['visiteur_session']['prefs'])) {
1402 $prefs = $GLOBALS['visiteur_session']['prefs'];
1406 if (is_string($prefs)) {
1407 $prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1410 ((isset($prefs['theme']) and $theme = $prefs['theme'])
1411 or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1412 and $theme != _SPIP_THEME_PRIVE
1414 array_unshift($themes, $theme);
1415 } // placer le theme choisi en tete
1421 function find_in_theme($file, $subdir = '', $include = false) {
1422 static $themefiles = array();
1423 if (isset($themefiles["$subdir$file"])) {
1424 return $themefiles["$subdir$file"];
1426 $themes = lister_themes_prives();
1427 foreach ($themes as $theme) {
1428 if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1429 return $themefiles["$subdir$file"] = $f;
1432 spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1434 return $themefiles["$subdir$file"] = "";
1439 * Cherche une image dans les dossiers d'images
1441 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1442 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1443 * de facon temporaire le temps de la migration, et cherche de nouveau.
1445 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1446 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1448 * @see find_in_theme()
1449 * @see inc_icone_renommer_dist()
1451 * @param string $icone
1452 * Nom de l'icone cherchée
1454 * Chemin complet de l'icone depuis la racine si l'icone est trouée,
1455 * sinon chaîne vide.
1457 function chemin_image($icone) {
1458 static $icone_renommer;
1459 // gerer le cas d'un double appel en evitant de refaire le travail inutilement
1460 if (strpos($icone, "/") !== false and file_exists($icone)) {
1464 // si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1465 if (preg_match(',[.](png|gif|jpg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1468 // sinon passer par le module de renommage
1469 if (is_null($icone_renommer)) {
1470 $icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1472 if ($icone_renommer) {
1473 list($icone, $fonction) = $icone_renommer($icone, "");
1474 if (file_exists($icone)) {
1479 return find_in_path($icone, _NOM_IMG_PACK
);
1483 // chercher un fichier $file dans le SPIP_PATH
1484 // si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1485 // si 3e arg vrai, on inclut si ce n'est fait.
1486 $GLOBALS['path_sig'] = '';
1487 $GLOBALS['path_files'] = null;
1490 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1492 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1493 * suivant l'ordre des chemins connus de SPIP.
1496 * @see charger_fonction()
1497 * @uses creer_chemin() Pour la liste des chemins.
1500 * $f = find_in_path('css/perso.css');
1501 * $f = find_in_path('perso.css', 'css');
1504 * @param string $file
1506 * @param string $dirname
1507 * Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1508 * @param bool|string $include
1509 * - false : ne fait rien de plus
1510 * - true : inclut le fichier (include_once)
1511 * - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1512 * @return string|bool
1513 * - string : chemin du fichier trouvé
1514 * - false : fichier introuvable
1516 function find_in_path($file, $dirname = '', $include = false) {
1517 static $dirs = array();
1518 static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
1521 // on calcule le chemin si le dossier skel a change
1522 if ($c != $GLOBALS['dossier_squelettes']) {
1523 // assurer le non plantage lors de la montee de version :
1524 $c = $GLOBALS['dossier_squelettes'];
1525 creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1528 if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1529 if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1532 if ($include and !isset($inc[$dirname][$file])) {
1533 include_once _ROOT_CWD
. $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1534 $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1537 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1540 $a = strrpos($file, '/');
1542 $dirname .= substr($file, 0, ++
$a);
1543 $file = substr($file, $a);
1546 foreach (creer_chemin() as $dir) {
1547 if (!isset($dirs[$a = $dir . $dirname])) {
1548 $dirs[$a] = (is_dir(_ROOT_CWD
. $a) ||
!$a);
1551 if (file_exists(_ROOT_CWD
. ($a .= $file))) {
1552 if ($include and !isset($inc[$dirname][$file])) {
1553 include_once _ROOT_CWD
. $a;
1554 $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1556 if (!defined('_SAUVER_CHEMIN')) {
1557 // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1558 if (is_null($GLOBALS['path_files'])) {
1561 define('_SAUVER_CHEMIN', true);
1564 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1570 spip_log("include_spip $dirname$file non trouve");
1571 if ($include === 'required') {
1573 "<strong>Erreur Fatale</strong><br />";
1574 if (function_exists('debug_print_backtrace')) {
1575 echo debug_print_backtrace();
1578 die("Erreur interne: ne peut inclure $dirname$file");
1582 if (!defined('_SAUVER_CHEMIN')) {
1583 // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1584 if (is_null($GLOBALS['path_files'])) {
1587 define('_SAUVER_CHEMIN', true);
1590 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1593 function clear_path_cache() {
1594 $GLOBALS['path_files'] = array();
1595 spip_unlink(_CACHE_CHEMIN
);
1598 function load_path_cache() {
1599 // charger le path des plugins
1600 if (@is_readable
(_CACHE_PLUGINS_PATH
)) {
1601 include_once(_CACHE_PLUGINS_PATH
);
1603 $GLOBALS['path_files'] = array();
1604 // si le visiteur est admin,
1605 // on ne recharge pas le cache pour forcer sa mise a jour
1607 // la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1608 //AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1609 // utiliser le cookie est un pis aller qui marche 'en general'
1610 // on blinde par un second test au moment de la lecture de la session
1611 // !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1612 // et en ignorant ce cache en cas de recalcul explicite
1613 !_request('var_mode')
1615 // on essaye de lire directement sans verrou pour aller plus vite
1616 if ($contenu = spip_file_get_contents(_CACHE_CHEMIN
)) {
1617 // mais si semble corrompu on relit avec un verrou
1618 if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1619 lire_fichier(_CACHE_CHEMIN
, $contenu);
1620 if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1621 $GLOBALS['path_files'] = array();
1628 function save_path_cache() {
1629 if (defined('_SAUVER_CHEMIN')
1632 ecrire_fichier(_CACHE_CHEMIN
, serialize($GLOBALS['path_files']));
1638 * Trouve tous les fichiers du path correspondants à un pattern
1640 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1641 * par un `find_in_path()`
1644 * @uses creer_chemin()
1645 * @uses preg_files()
1647 * @param string $dir
1648 * @param string $pattern
1649 * @param bool $recurs
1652 function find_all_in_path($dir, $pattern, $recurs = false) {
1653 $liste_fichiers = array();
1656 // cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1657 // on a pas encore inclus flock.php
1658 if (!function_exists('preg_files')) {
1659 include_once _ROOT_RESTREINT
. 'inc/flock.php';
1662 // Parcourir le chemin
1663 foreach (creer_chemin() as $d) {
1666 $liste = preg_files($f, $pattern, $maxfiles - count($liste_fichiers), $recurs === true ?
array() : $recurs);
1667 foreach ($liste as $chemin) {
1668 $nom = basename($chemin);
1669 // ne prendre que les fichiers pas deja trouves
1670 // car find_in_path prend le premier qu'il trouve,
1671 // les autres sont donc masques
1672 if (!isset($liste_fichiers[$nom])) {
1673 $liste_fichiers[$nom] = $chemin;
1679 return $liste_fichiers;
1683 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1684 * et beneficient d'une exception
1686 * @param string $nom
1687 * @param bool $strict
1690 function autoriser_sans_cookie($nom, $strict = false) {
1691 static $autsanscookie = array('install', 'base_repair');
1693 if (in_array($nom, $autsanscookie)) {
1694 if (test_espace_prive()){
1695 include_spip('base/connect_sql');
1696 if (!$strict or !spip_connect()){
1705 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1709 * numero de la cle primaire si nombre, URL a decoder si pas numerique
1710 * @param string $entite
1711 * surnom de la table SQL (donne acces au nom de cle primaire)
1712 * @param string $args
1713 * query_string a placer apres cle=$id&....
1714 * @param string $ancre
1715 * ancre a mettre a la fin de l'URL a produire
1716 * @param bool|string $public
1717 * produire l'URL publique ou privee (par defaut: selon espace)
1718 * si string : serveur de base de donnee (nom du connect)
1719 * @param string $type
1720 * fichier dans le repertoire ecrire/urls determinant l'apparence
1721 * @return string|array
1722 * url codee ou fonction de decodage
1723 * array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1724 * (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1726 function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1727 if ($public === null) {
1728 $public = !test_espace_prive();
1730 $entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1736 if (!function_exists('generer_url_ecrire_objet')) {
1737 include_spip('inc/urls');
1739 $res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1741 if ($type === null) {
1742 $type = (isset($GLOBALS['type_urls']))
1743 ?
$GLOBALS['type_urls'] // pour surcharge via fichier d'options
1744 : ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1745 ?
($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1748 $f = charger_fonction($type, 'urls', true);
1749 // se rabattre sur les urls page si les urls perso non dispo
1751 $f = charger_fonction('page', 'urls', true);
1754 // si $entite='', on veut la fonction de passage URL ==> id
1755 // sinon on veut effectuer le passage id ==> URL
1760 // mais d'abord il faut tester le cas des urls sur une
1762 if (is_string($public)
1763 and $g = charger_fonction('connect', 'urls', true)
1768 $res = $f(intval($id), $entite, $args, $ancre, $public);
1774 // Sinon c'est un raccourci ou compat SPIP < 2
1775 if (!function_exists($f = 'generer_url_' . $entite)) {
1776 if (!function_exists($f .= '_dist')) {
1781 $url = $f($id, $args, $ancre);
1782 if (strlen($args)) {
1783 $url .= strstr($url, '?')
1790 // On a ete gentil mais la ....
1791 spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1796 function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1797 $exec = objet_info($entite, 'url_edit');
1798 $url = generer_url_ecrire($exec, $args);
1800 $url = parametre_url($url, id_table_objet($entite), $id);
1802 $url = parametre_url($url, 'new', 'oui');
1805 $url = ancre_url($url, $ancre);
1811 // http://code.spip.net/@urls_connect_dist
1812 function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1813 include_spip('base/connect_sql');
1814 $id_type = id_table_objet($entite, $public);
1816 return _DIR_RACINE
. get_spip_script('./')
1817 . "?" . _SPIP_PAGE
. "=$entite&$id_type=$i&connect=$public"
1818 . (!$args ?
'' : "&$args")
1819 . (!$ancre ?
'' : "#$ancre");
1824 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1826 * @param string $url
1829 function urlencode_1738($url) {
1830 if (preg_match(',[^\x00-\x7E],sS', $url)) {
1832 for ($i = 0; $i < strlen($url); $i++
) {
1833 if (ord($a = $url[$i]) > 127) {
1834 $a = rawurlencode($a);
1841 return quote_amp($url);
1844 // http://code.spip.net/@generer_url_entite_absolue
1845 function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1849 $h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1850 if (!preg_match(',^\w+:,', $h)) {
1851 include_spip('inc/filtres_mini');
1852 $h = url_absolue($h);
1860 * Tester qu'une variable d'environnement est active
1862 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1863 * variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
1865 * @param string|bool $truc
1866 * La valeur de la variable d'environnement
1868 * true si la valeur est considérée active ; false sinon.
1870 function test_valeur_serveur($truc) {
1875 return (strtolower($truc) !== 'off');
1879 // Fonctions de fabrication des URL des scripts de Spip
1882 * Calcule l'url de base du site
1884 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1885 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1886 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1889 * La globale `$profondeur_url` doit être initialisée de manière à
1890 * indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1891 * racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1892 * la racine 0. Sur url/perso/ elle vaut 2
1894 * @param int|boo|array $profondeur
1895 * - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1896 * - si int : indique que l'on veut l'url pour la profondeur indiquée
1897 * - si bool : retourne le tableau static complet
1898 * - si array : réinitialise le tableau static complet avec la valeur fournie
1899 * @return string|array
1901 function url_de_base($profondeur = null) {
1903 static $url = array();
1904 if (is_array($profondeur)) {
1905 return $url = $profondeur;
1907 if ($profondeur === false) {
1911 if (is_null($profondeur)) {
1912 $profondeur = $GLOBALS['profondeur_url'];
1915 if (isset($url[$profondeur])) {
1916 return $url[$profondeur];
1922 isset($_SERVER["SCRIPT_URI"])
1923 and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1927 isset($_SERVER['HTTPS'])
1928 and test_valeur_serveur($_SERVER['HTTPS'])
1933 // note : HTTP_HOST contient le :port si necessaire
1934 $host = isset($_SERVER['HTTP_HOST']) ?
$_SERVER['HTTP_HOST'] : null;
1935 // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1936 if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1937 $host = $GLOBALS['meta']['adresse_site'];
1938 if ($scheme = parse_url($host, PHP_URL_SCHEME
)) {
1940 $host = str_replace("{$scheme}://", '', $host);
1943 if (isset($_SERVER['SERVER_PORT'])
1944 and $port = $_SERVER['SERVER_PORT']
1945 and strpos($host, ":") == false
1947 if (!defined('_PORT_HTTP_STANDARD')) {
1948 define('_PORT_HTTP_STANDARD', '80');
1950 if (!defined('_PORT_HTTPS_STANDARD')) {
1951 define('_PORT_HTTPS_STANDARD', '443');
1953 if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD
))) {
1956 if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD
))) {
1961 if (!$GLOBALS['REQUEST_URI']) {
1962 if (isset($_SERVER['REQUEST_URI'])) {
1963 $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1965 $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ?
$_SERVER['PHP_SELF'] : '';
1966 if (!empty($_SERVER['QUERY_STRING'])
1967 and !strpos($_SERVER['REQUEST_URI'], '?')
1969 $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1974 $url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1976 return $url[$profondeur];
1980 * fonction testable de construction d'une url appelee par url_de_base()
1982 * @param string $http
1983 * @param string $host
1984 * @param string $request
1988 function url_de_($http, $host, $request, $prof = 0) {
1989 $prof = max($prof, 0);
1991 $myself = ltrim($request, '/');
1992 # supprimer la chaine de GET
1993 list($myself) = explode('?', $myself);
1994 // vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1995 // protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1996 if (strpos($myself,'://') !== false) {
1997 $myself = explode('://',$myself);
1998 array_shift($myself);
1999 $myself = implode('://',$myself);
2000 $myself = explode('/',$myself);
2001 array_shift($myself);
2002 $myself = implode('/',$myself);
2004 $url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
2006 $url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
2012 // Pour une redirection, la liste des arguments doit etre separee par "&"
2013 // Pour du code XHTML, ca doit etre &
2014 // Bravo au W3C qui n'a pas ete capable de nous eviter ca
2015 // faute de separer proprement langage et meta-langage
2017 // Attention, X?y=z et "X/?y=z" sont completement differents!
2018 // http://httpd.apache.org/docs/2.0/mod/mod_dir.html
2021 * Crée une URL vers un script de l'espace privé
2025 * generer_url_ecrire('admin_plugin')
2028 * @param string $script
2029 * Nom de la page privée (xx dans exec=xx)
2030 * @param string $args
2031 * Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2032 * @param bool $no_entities
2033 * Si false : transforme les `&` en `&`
2034 * @param bool|string $rel
2037 * - false : l’URL sera complète et contiendra l’URL du site
2038 * - true : l’URL sera relavive.
2039 * - string : on transmet l'url à la fonction
2040 * @return string URL
2042 function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2044 $rel = url_de_base() . _DIR_RESTREINT_ABS
. _SPIP_ECRIRE_SCRIPT
;
2046 if (!is_string($rel)) {
2047 $rel = _DIR_RESTREINT ? _DIR_RESTREINT
:
2048 ('./' . _SPIP_ECRIRE_SCRIPT
);
2052 list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2053 if ($script and ($script <> 'accueil' or $rel)) {
2054 $args = "?exec=$script" . (!$args ?
'' : "&$args");
2062 return $rel . ($no_entities ?
$args : str_replace('&', '&', $args));
2066 * Permet d'ajouter lien vers une page privée à un paramètre d'url (déprécié)
2070 * $h = generer_url_ecrire('article', "id_article=$id_article&redirect=" . generer_url_retour('articles'));
2072 * $h = generer_url_ecrire('article');
2073 * $h = parametre_url($h, 'id_article', $id_article);
2074 * $h = parametre_url($h, 'redirect', generer_url_ecrire('articles'));
2077 * @deprecated Utiliser parametre_url() et generer_url_ecrire()
2078 * @see parametre_url()
2079 * @see generer_url_ecrire()
2081 * @param string $script
2082 * @param string $args
2085 function generer_url_retour($script, $args = "") {
2086 return rawurlencode(generer_url_ecrire($script, $args, true, true));
2090 // Adresse des scripts publics (a passer dans inc-urls...)
2095 * Retourne le nom du fichier d'exécution de SPIP
2099 * Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2100 * dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2102 * @param string $default
2105 * Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2107 function get_spip_script($default = '') {
2108 # cas define('_SPIP_SCRIPT', '');
2110 return _SPIP_SCRIPT
;
2117 * Crée une URL vers une page publique de SPIP
2121 * generer_url_public("rubrique","id_rubrique=$id_rubrique")
2124 * @param string $script
2126 * @param string|array $args
2127 * Arguments à transmettre a l'URL,
2128 * soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2129 * soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2130 * @param bool $no_entities
2131 * Si false : transforme les `&` en `&`
2135 * - false : l’URL sera complète et contiendra l’URL du site
2136 * - true : l’URL sera relavive.
2137 * @param string $action
2138 * - Fichier d'exécution public (spip.php par défaut)
2139 * @return string URL
2141 function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2142 // si le script est une action (spip_pass, spip_inscription),
2143 // standardiser vers la nouvelle API
2146 $action = get_spip_script();
2149 $action = parametre_url($action, _SPIP_PAGE
, $script, '&');
2153 if (is_array($args)) {
2155 foreach ($args as $k => $v) {
2156 $r .= '&' . $k . '=' . $v;
2158 $args = substr($r, 1);
2161 (strpos($action, '?') !== false ?
'&' : '?') . $args;
2163 if (!$no_entities) {
2164 $action = quote_amp($action);
2167 // ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2168 return ($rel ? _DIR_RACINE
. $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2171 // http://code.spip.net/@generer_url_prive
2172 function generer_url_prive($script, $args = "", $no_entities = false) {
2174 return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS
. 'prive.php');
2177 // Pour les formulaires en methode POST,
2178 // mettre le nom du script a la fois en input-hidden et dans le champ action:
2179 // 1) on peut ainsi memoriser le signet comme si c'etait un GET
2180 // 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2183 * Retourne un formulaire (POST par défaut) vers un script exec
2184 * de l’interface privée
2186 * @param string $script
2187 * Nom de la page exec
2188 * @param string $corps
2189 * Contenu du formulaire
2190 * @param string $atts
2191 * Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2192 * @param string $submit
2193 * Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2195 * Code HTML du formulaire
2197 function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2199 $script1 = explode('&', $script);
2200 $script1 = reset($script1);
2202 return "<form action='"
2203 . ($script ?
generer_url_ecrire($script) : '')
2205 . ($atts ?
$atts : " method='post'")
2207 . "<input type='hidden' name='exec' value='$script1' />"
2210 ("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2211 . "</div></form>\n";
2215 * Générer un formulaire pour lancer une action vers $script
2217 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2218 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2219 * qui ne sont pas destines a etre mis en signets
2221 * @param string $script
2222 * @param string $corps
2223 * @param string $atts
2224 * @param bool $public
2227 function generer_form_action($script, $corps, $atts = '', $public = false) {
2228 // si l'on est dans l'espace prive, on garde dans l'url
2229 // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2230 // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2231 $h = (_DIR_RACINE
and !$public)
2232 ?
generer_url_ecrire(_request('exec'))
2233 : generer_url_public();
2235 return "\n<form action='" .
2241 "\n<input type='hidden' name='action' value='$script' />" .
2249 * @param string $script
2250 * Nom du script à exécuter
2251 * @param string $args
2252 * Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2253 * @param bool $no_entities
2254 * Si false : transforme les & en &
2255 * @param boolean $public
2256 * URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2257 * true : l’URL sera relative.
2261 function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2262 // si l'on est dans l'espace prive, on garde dans l'url
2263 // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2264 // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2265 $url = (_DIR_RACINE
and !$public)
2266 ?
generer_url_ecrire(_request('exec'))
2267 : generer_url_public('', '', false, false);
2268 $url = parametre_url($url, 'action', $script);
2270 $url .= quote_amp('&' . $args);
2274 $url = str_replace('&', '&', $url);
2282 * Fonction d'initialisation groupée pour compatibilité ascendante
2284 * @param string $pi Répertoire permanent inaccessible
2285 * @param string $pa Répertoire permanent accessible
2286 * @param string $ti Répertoire temporaire inaccessible
2287 * @param string $ta Répertoire temporaire accessible
2289 function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2290 spip_initialisation_core($pi, $pa, $ti, $ta);
2291 spip_initialisation_suite();
2295 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2297 * Elle définit les répertoires et fichiers non partageables
2298 * et indique dans $test_dirs ceux devant être accessibles en écriture
2299 * mais ne touche pas à cette variable si elle est déjà définie
2300 * afin que mes_options.php puisse en spécifier d'autres.
2302 * Elle définit ensuite les noms des fichiers et les droits.
2303 * Puis simule un register_global=on sécurisé.
2305 * @param string $pi Répertoire permanent inaccessible
2306 * @param string $pa Répertoire permanent accessible
2307 * @param string $ti Répertoire temporaire inaccessible
2308 * @param string $ta Répertoire temporaire accessible
2310 function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2311 static $too_late = 0;
2316 // Declaration des repertoires
2318 // le nom du repertoire plugins/ activables/desactivables
2319 if (!defined('_DIR_PLUGINS')) {
2320 define('_DIR_PLUGINS', _DIR_RACINE
. "plugins/");
2323 // le nom du repertoire des extensions/ permanentes du core, toujours actives
2324 if (!defined('_DIR_PLUGINS_DIST')) {
2325 define('_DIR_PLUGINS_DIST', _DIR_RACINE
. "plugins-dist/");
2328 // le nom du repertoire des librairies
2329 if (!defined('_DIR_LIB')) {
2330 define('_DIR_LIB', _DIR_RACINE
. "lib/");
2333 if (!defined('_DIR_IMG')) {
2334 define('_DIR_IMG', $pa);
2336 if (!defined('_DIR_LOGOS')) {
2337 define('_DIR_LOGOS', $pa);
2339 if (!defined('_DIR_IMG_ICONES')) {
2340 define('_DIR_IMG_ICONES', _DIR_LOGOS
. "icones/");
2343 if (!defined('_DIR_DUMP')) {
2344 define('_DIR_DUMP', $ti . "dump/");
2346 if (!defined('_DIR_SESSIONS')) {
2347 define('_DIR_SESSIONS', $ti . "sessions/");
2349 if (!defined('_DIR_TRANSFERT')) {
2350 define('_DIR_TRANSFERT', $ti . "upload/");
2352 if (!defined('_DIR_CACHE')) {
2353 define('_DIR_CACHE', $ti . "cache/");
2355 if (!defined('_DIR_CACHE_XML')) {
2356 define('_DIR_CACHE_XML', _DIR_CACHE
. "xml/");
2358 if (!defined('_DIR_SKELS')) {
2359 define('_DIR_SKELS', _DIR_CACHE
. "skel/");
2361 if (!defined('_DIR_AIDE')) {
2362 define('_DIR_AIDE', _DIR_CACHE
. "aide/");
2364 if (!defined('_DIR_TMP')) {
2365 define('_DIR_TMP', $ti);
2368 if (!defined('_DIR_VAR')) {
2369 define('_DIR_VAR', $ta);
2372 if (!defined('_DIR_ETC')) {
2373 define('_DIR_ETC', $pi);
2375 if (!defined('_DIR_CONNECT')) {
2376 define('_DIR_CONNECT', $pi);
2378 if (!defined('_DIR_CHMOD')) {
2379 define('_DIR_CHMOD', $pi);
2382 if (!isset($GLOBALS['test_dirs']))
2383 // Pas $pi car il est bon de le mettre hors ecriture apres intstall
2384 // il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2386 $GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2389 // Declaration des fichiers
2391 if (!defined('_CACHE_PLUGINS_PATH')) {
2392 define('_CACHE_PLUGINS_PATH', _DIR_CACHE
. "charger_plugins_chemins.php");
2394 if (!defined('_CACHE_PLUGINS_OPT')) {
2395 define('_CACHE_PLUGINS_OPT', _DIR_CACHE
. "charger_plugins_options.php");
2397 if (!defined('_CACHE_PLUGINS_FCT')) {
2398 define('_CACHE_PLUGINS_FCT', _DIR_CACHE
. "charger_plugins_fonctions.php");
2400 if (!defined('_CACHE_PIPELINES')) {
2401 define('_CACHE_PIPELINES', _DIR_CACHE
. "charger_pipelines.php");
2403 if (!defined('_CACHE_CHEMIN')) {
2404 define('_CACHE_CHEMIN', _DIR_CACHE
. "chemin.txt");
2407 # attention .php obligatoire pour ecrire_fichier_securise
2408 if (!defined('_FILE_META')) {
2409 define('_FILE_META', $ti . 'meta_cache.php');
2411 if (!defined('_DIR_LOG')) {
2412 define('_DIR_LOG', _DIR_TMP
. 'log/');
2414 if (!defined('_FILE_LOG')) {
2415 define('_FILE_LOG', 'spip');
2417 if (!defined('_FILE_LOG_SUFFIX')) {
2418 define('_FILE_LOG_SUFFIX', '.log');
2421 // Le fichier de connexion a la base de donnees
2422 // tient compte des anciennes versions (inc_connect...)
2423 if (!defined('_FILE_CONNECT_INS')) {
2424 define('_FILE_CONNECT_INS', 'connect');
2426 if (!defined('_FILE_CONNECT')) {
2427 define('_FILE_CONNECT',
2428 (@is_readable
($f = _DIR_CONNECT
. _FILE_CONNECT_INS
. '.php') ?
$f
2429 : (@is_readable
($f = _DIR_RESTREINT
. 'inc_connect.php') ?
$f
2433 // Le fichier de reglages des droits
2434 if (!defined('_FILE_CHMOD_INS')) {
2435 define('_FILE_CHMOD_INS', 'chmod');
2437 if (!defined('_FILE_CHMOD')) {
2438 define('_FILE_CHMOD',
2439 (@is_readable
($f = _DIR_CHMOD
. _FILE_CHMOD_INS
. '.php') ?
$f
2443 if (!defined('_FILE_LDAP')) {
2444 define('_FILE_LDAP', 'ldap.php');
2447 if (!defined('_FILE_TMP_SUFFIX')) {
2448 define('_FILE_TMP_SUFFIX', '.tmp.php');
2450 if (!defined('_FILE_CONNECT_TMP')) {
2451 define('_FILE_CONNECT_TMP', _DIR_CONNECT
. _FILE_CONNECT_INS
. _FILE_TMP_SUFFIX
);
2453 if (!defined('_FILE_CHMOD_TMP')) {
2454 define('_FILE_CHMOD_TMP', _DIR_CHMOD
. _FILE_CHMOD_INS
. _FILE_TMP_SUFFIX
);
2457 // Definition des droits d'acces en ecriture
2458 if (!defined('_SPIP_CHMOD') and _FILE_CHMOD
) {
2459 include_once _FILE_CHMOD
;
2462 // Se mefier des fichiers mal remplis!
2463 if (!defined('_SPIP_CHMOD')) {
2464 define('_SPIP_CHMOD', 0777);
2467 if (!defined('_DEFAULT_CHARSET')) {
2468 /** Le charset par défaut lors de l'installation */
2469 define('_DEFAULT_CHARSET', 'utf-8');
2471 if (!defined('_ROOT_PLUGINS')) {
2472 define('_ROOT_PLUGINS', _ROOT_RACINE
. "plugins/");
2474 if (!defined('_ROOT_PLUGINS_DIST')) {
2475 define('_ROOT_PLUGINS_DIST', _ROOT_RACINE
. "plugins-dist/");
2477 if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL
) {
2478 define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE
. str_replace(_DIR_RACINE
, '', _DIR_PLUGINS_SUPPL
));
2481 // La taille des Log
2482 if (!defined('_MAX_LOG')) {
2483 define('_MAX_LOG', 100);
2486 // Sommes-nous dans l'empire du Mal ?
2487 // (ou sous le signe du Pingouin, ascendant GNU ?)
2488 if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2489 if (!defined('_OS_SERVEUR')) {
2490 define('_OS_SERVEUR', 'windows');
2492 if (!defined('_SPIP_LOCK_MODE')) {
2493 define('_SPIP_LOCK_MODE', 1);
2494 } // utiliser le flock php
2496 if (!defined('_OS_SERVEUR')) {
2497 define('_OS_SERVEUR', '');
2499 if (!defined('_SPIP_LOCK_MODE')) {
2500 define('_SPIP_LOCK_MODE', 1);
2501 } // utiliser le flock php
2502 #if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2505 // Langue par defaut
2506 if (!defined('_LANGUE_PAR_DEFAUT')) {
2507 define('_LANGUE_PAR_DEFAUT', 'fr');
2511 // Module de lecture/ecriture/suppression de fichiers utilisant flock()
2512 // (non surchargeable en l'etat ; attention si on utilise include_spip()
2513 // pour le rendre surchargeable, on va provoquer un reecriture
2514 // systematique du noyau ou une baisse de perfs => a etudier)
2515 include_once _ROOT_RESTREINT
. 'inc/flock.php';
2517 // charger tout de suite le path et son cache
2520 // *********** traiter les variables ************
2526 // Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2527 if (isset($_REQUEST['GLOBALS'])) {
2530 // nettoyer les magic quotes \' et les caracteres nuls %00
2531 spip_desinfecte($_GET);
2532 spip_desinfecte($_POST);
2533 spip_desinfecte($_COOKIE);
2534 spip_desinfecte($_REQUEST);
2536 // Si les variables sont passees en global par le serveur,
2537 // il faut faire quelques verifications de base
2538 // Todo: test à supprimer lorsque version PHP minimum >= 5.4.
2539 $avertir_register_globals = false;
2540 if (test_valeur_serveur(@ini_get
('register_globals'))) {
2541 // ne pas desinfecter les globales en profondeur car elle contient aussi les
2542 // precedentes, qui seraient desinfectees 2 fois.
2543 spip_desinfecte($GLOBALS, false);
2545 if (include_spip('inc/php3')) {
2546 spip_register_globals(true);
2549 $avertir_register_globals = true;
2552 // appliquer le cookie_prefix
2553 if ($GLOBALS['cookie_prefix'] != 'spip') {
2554 include_spip('inc/cookie');
2555 recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2559 // Capacites php (en fonction de la version)
2561 $GLOBALS['flag_ob'] = (function_exists("ob_start")
2562 && function_exists("ini_get")
2563 && !strstr(@ini_get
('disable_functions'), 'ob_'));
2564 $GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2565 $GLOBALS['flag_get_cfg_var'] = (@get_cfg_var
('error_reporting') != "");
2566 $GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2567 (get_cfg_var('upload_max_filesize') > 0));
2570 // Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2571 if (isset($_SERVER['REQUEST_URI'])) {
2572 $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2574 $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ?
$_SERVER['PHP_SELF'] : '';
2575 if (!empty($_SERVER['QUERY_STRING'])
2576 and !strpos($_SERVER['REQUEST_URI'], '?')
2578 $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2582 // Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2583 if (!defined('_RENOUVELLE_ALEA')) {
2584 define('_RENOUVELLE_ALEA', 12 * 3600);
2586 if (!defined('_DUREE_COOKIE_ADMIN')) {
2587 define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2590 // charger les meta si possible et renouveller l'alea au besoin
2591 // charge aussi effacer_meta et ecrire_meta
2592 $inc_meta = charger_fonction('meta', 'inc');
2595 // on a pas pu le faire plus tot
2596 if ($avertir_register_globals) {
2597 avertir_auteurs("register_globals",
2598 _L("Problème de sécurité : register_globals=on; dans php.ini à corriger."));
2601 // nombre de repertoires depuis la racine
2602 // on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2603 // ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2604 // le calcul est faux)
2605 if (!_DIR_RESTREINT
) {
2606 $GLOBALS['profondeur_url'] = 1;
2608 $uri = isset($_SERVER['REQUEST_URI']) ?
explode('?', $_SERVER['REQUEST_URI']) : '';
2609 $uri_ref = $_SERVER["SCRIPT_NAME"];
2611 // si on est appele avec un autre ti, on est sans doute en mutu
2612 // si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2613 // a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2614 // s'en remettre a l'adresse du site. alea jacta est.
2615 or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2618 if (isset($GLOBALS['meta']['adresse_site'])) {
2619 $uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2620 $uri_ref = (isset($uri_ref['path']) ?
$uri_ref['path'] : '') . '/';
2625 if (!$uri or !$uri_ref) {
2626 $GLOBALS['profondeur_url'] = 0;
2628 $GLOBALS['profondeur_url'] = max(0,
2629 substr_count($uri[0], '/')
2630 - substr_count($uri_ref, '/'));
2633 // s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2634 if (_FILE_CONNECT
) {
2635 if (verifier_visiteur() == '0minirezo'
2636 // si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2637 and !isset($_COOKIE['spip_admin'])
2646 * Complements d'initialisation non critiques pouvant etre realises
2650 function spip_initialisation_suite() {
2651 static $too_late = 0;
2656 // taille mini des login
2657 if (!defined('_LOGIN_TROP_COURT')) {
2658 define('_LOGIN_TROP_COURT', 4);
2661 // la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2662 #if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2663 #if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2664 #if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2666 // la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2667 #if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2668 #if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2669 #if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2670 #if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2672 if (!defined('_PASS_LONGUEUR_MINI')) {
2673 define('_PASS_LONGUEUR_MINI', 6);
2677 // Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2678 if (!defined('_IMG_QUALITE')) {
2679 define('_IMG_QUALITE', 85);
2680 } # valeur par defaut
2681 if (!defined('_IMG_GD_QUALITE')) {
2682 define('_IMG_GD_QUALITE', _IMG_QUALITE
);
2683 } # surcharge pour la lib GD
2684 if (!defined('_IMG_CONVERT_QUALITE')) {
2685 define('_IMG_CONVERT_QUALITE', _IMG_QUALITE
);
2686 } # surcharge pour imagick en ligne de commande
2687 // Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2688 if (!defined('_IMG_IMAGICK_QUALITE')) {
2689 define('_IMG_IMAGICK_QUALITE', 75);
2690 } # surcharge pour imagick en PHP
2692 if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2693 define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2696 // qq chaines standard
2697 if (!defined('_ACCESS_FILE_NAME')) {
2698 define('_ACCESS_FILE_NAME', '.htaccess');
2700 if (!defined('_AUTH_USER_FILE')) {
2701 define('_AUTH_USER_FILE', '.htpasswd');
2703 if (!defined('_SPIP_DUMP')) {
2704 define('_SPIP_DUMP', 'dump@nom_site@@stamp@.xml');
2706 if (!defined('_CACHE_RUBRIQUES')) {
2707 /** Fichier cache pour le navigateur de rubrique du bandeau */
2708 define('_CACHE_RUBRIQUES', _DIR_TMP
. 'menu-rubriques-cache.txt');
2710 if (!defined('_CACHE_RUBRIQUES_MAX')) {
2711 /** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2712 define('_CACHE_RUBRIQUES_MAX', 500);
2715 if (!defined('_EXTENSION_SQUELETTES')) {
2716 define('_EXTENSION_SQUELETTES', 'html');
2719 if (!defined('_DOCTYPE_ECRIRE')) {
2720 define('_DOCTYPE_ECRIRE',
2721 // "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
2722 //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
2723 //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
2724 // "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1 //EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>\n");
2725 "<!DOCTYPE html>\n");
2727 if (!defined('_DOCTYPE_AIDE')) {
2728 define('_DOCTYPE_AIDE',
2729 "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2732 /** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2733 * le script de l'espace public, alias index.php */
2734 if (!defined('_SPIP_SCRIPT')) {
2735 define('_SPIP_SCRIPT', 'spip.php');
2737 /** Argument page, personalisable en cas de conflit avec un autre script */
2738 if (!defined('_SPIP_PAGE')) {
2739 define('_SPIP_PAGE', 'page');
2742 // le script de l'espace prive
2743 // Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2744 // les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2745 // meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2746 if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2747 if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
2748 define('_SPIP_ECRIRE_SCRIPT', 'index.php');
2750 define('_SPIP_ECRIRE_SCRIPT', '');
2755 if (!defined('_SPIP_AJAX')) {
2756 define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2758 : (($_COOKIE['spip_accepte_ajax'] != -1) ?
1 : 0)));
2761 // La requete est-elle en ajax ?
2762 if (!defined('_AJAX')) {
2764 (isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2765 or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2766 or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2767 or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2769 and empty($_REQUEST['var_noajax']) # horrible exception, car c'est pas parce que la requete est ajax jquery qu'il faut tuer tous les formulaires ajax qu'elle contient
2773 # nombre de pixels maxi pour calcul de la vignette avec gd
2774 # au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2775 # les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2776 if (!defined('_IMG_GD_MAX_PIXELS')) {
2777 define('_IMG_GD_MAX_PIXELS',
2778 (isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2779 ?
$GLOBALS['meta']['max_taille_vignettes']
2783 if (!defined('_MEMORY_LIMIT_MIN')) {
2784 define('_MEMORY_LIMIT_MIN', 16);
2786 // si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2787 // on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2788 // il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2789 if (test_espace_prive() and _MEMORY_LIMIT_MIN
> 8) {
2790 if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2791 $unit = strtolower(substr($memory, -1));
2792 $memory = substr($memory, 0, -1);
2794 // Le modifieur 'G' est disponible depuis PHP 5.1.0
2802 if ($memory < _MEMORY_LIMIT_MIN
* 1024 * 1024) {
2803 @ini_set
('memory_limit', $m = _MEMORY_LIMIT_MIN
. 'M');
2804 if (trim(ini_get('memory_limit')) != $m) {
2805 if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2806 define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2807 } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2811 if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2812 define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2814 } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2816 // Protocoles a normaliser dans les chaines de langues
2817 if (!defined('_PROTOCOLES_STD')) {
2818 define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2825 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2826 * la validité du cache ou certains affichages spéciaux.
2828 * Le paramètre d'URL `var_mode` permet de
2829 * modifier la pérennité du cache, recalculer des urls
2830 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2831 * d'afficher un écran de débug ou des traductions non réalisées.
2833 * En fonction de ces paramètres dans l'URL appelante, on définit
2834 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2836 * Le paramètre `var_mode` accepte ces valeurs :
2838 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2839 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2840 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2841 * - `debug` : modifie l'affichage activant le mode "debug"
2842 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2843 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2844 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2845 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2847 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2850 * Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2851 * le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2852 * ` var_mode` (calcul ou recalcul).
2854 function init_var_mode() {
2855 static $done = false;
2858 if (isset($_GET['var_mode'])) {
2859 $var_mode = explode(',', $_GET['var_mode']);
2860 // tout le monde peut calcul/recalcul
2861 if (!defined('_VAR_MODE')) {
2862 if (in_array('recalcul', $var_mode)) {
2863 define('_VAR_MODE', 'recalcul');
2864 } elseif (in_array('calcul', $var_mode)) {
2865 define('_VAR_MODE', 'calcul');
2868 $var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2870 include_spip('inc/autoriser');
2871 // autoriser preview si preview seulement, et sinon autoriser debug
2873 ($_GET['var_mode'] == 'preview')
2877 if (in_array('traduction', $var_mode)) {
2878 // forcer le calcul pour passer dans traduire
2879 if (!defined('_VAR_MODE')) {
2880 define('_VAR_MODE', 'calcul');
2882 // et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2883 if (!defined('_VAR_NOCACHE')) {
2884 define('_VAR_NOCACHE', true);
2886 $var_mode = array_diff($var_mode, array('traduction'));
2888 if (in_array('preview', $var_mode)) {
2889 // basculer sur les criteres de preview dans les boucles
2890 if (!defined('_VAR_PREVIEW')) {
2891 define('_VAR_PREVIEW', true);
2894 if (!defined('_VAR_MODE')) {
2895 define('_VAR_MODE', 'calcul');
2897 // et ne pas enregistrer de cache
2898 if (!defined('_VAR_NOCACHE')) {
2899 define('_VAR_NOCACHE', true);
2901 $var_mode = array_diff($var_mode, array('preview'));
2903 if (in_array('inclure', $var_mode)) {
2904 // forcer le compilo et ignorer les caches existants
2905 if (!defined('_VAR_MODE')) {
2906 define('_VAR_MODE', 'calcul');
2908 if (!defined('_VAR_INCLURE')) {
2909 define('_VAR_INCLURE', true);
2911 // et ne pas enregistrer de cache
2912 if (!defined('_VAR_NOCACHE')) {
2913 define('_VAR_NOCACHE', true);
2915 $var_mode = array_diff($var_mode, array('inclure'));
2917 if (in_array('urls', $var_mode)) {
2918 // forcer le compilo et ignorer les caches existants
2919 if (!defined('_VAR_MODE')) {
2920 define('_VAR_MODE', 'calcul');
2922 if (!defined('_VAR_URLS')) {
2923 define('_VAR_URLS', true);
2925 $var_mode = array_diff($var_mode, array('urls'));
2927 if (in_array('images', $var_mode)) {
2928 // forcer le compilo et ignorer les caches existants
2929 if (!defined('_VAR_MODE')) {
2930 define('_VAR_MODE', 'calcul');
2932 // indiquer qu'on doit recalculer les images
2933 if (!defined('_VAR_IMAGES')) {
2934 define('_VAR_IMAGES', true);
2936 $var_mode = array_diff($var_mode, array('images'));
2938 if (in_array('debug', $var_mode)) {
2939 if (!defined('_VAR_MODE')) {
2940 define('_VAR_MODE', 'debug');
2942 // et ne pas enregistrer de cache
2943 if (!defined('_VAR_NOCACHE')) {
2944 define('_VAR_NOCACHE', true);
2946 $var_mode = array_diff($var_mode, array('debug'));
2948 if (count($var_mode) and !defined('_VAR_MODE')) {
2949 define('_VAR_MODE', reset($var_mode));
2951 if (isset($GLOBALS['visiteur_session']['nom'])) {
2952 spip_log($GLOBALS['visiteur_session']['nom']
2957 // si on n'est pas connecte on se redirige
2958 if (!$GLOBALS['visiteur_session']) {
2959 include_spip('inc/headers');
2960 redirige_par_entete(generer_url_public('login',
2961 'url=' . rawurlencode(
2962 parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2969 if (!defined('_VAR_MODE')) {
2971 * Indique le mode de calcul ou d'affichage de la page.
2972 * @see init_var_mode()
2974 define('_VAR_MODE', false);
2980 // Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2981 // supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2982 // la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2983 // http://code.spip.net/@spip_desinfecte
2984 function spip_desinfecte(&$t, $deep = true) {
2985 static $magic_quotes;
2986 if (!isset($magic_quotes)) {
2987 $magic_quotes = @get_magic_quotes_gpc
();
2990 foreach ($t as $key => $val) {
2991 if (is_string($t[$key])) {
2992 if ($magic_quotes) {
2993 $t[$key] = stripslashes($t[$key]);
2995 $t[$key] = str_replace(chr(0), '-', $t[$key]);
2996 } // traiter aussi les "texte_plus" de article_edit
2998 if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2999 spip_desinfecte($t[$key], $deep);
3005 // retourne le statut du visiteur s'il s'annonce
3007 // http://code.spip.net/@verifier_visiteur
3008 function verifier_visiteur() {
3009 // Rq: pour que cette fonction marche depuis mes_options
3010 // il faut forcer l'init si ce n'est fait
3011 // mais on risque de perturber des plugins en initialisant trop tot
3012 // certaines constantes
3013 @spip_initialisation_core
(
3014 (_DIR_RACINE
. _NOM_PERMANENTS_INACCESSIBLES
),
3015 (_DIR_RACINE
. _NOM_PERMANENTS_ACCESSIBLES
),
3016 (_DIR_RACINE
. _NOM_TEMPORAIRES_INACCESSIBLES
),
3017 (_DIR_RACINE
. _NOM_TEMPORAIRES_ACCESSIBLES
)
3020 // Demarrer une session NON AUTHENTIFIEE si on donne son nom
3021 // dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
3022 // Attention on separe bien session_nom et nom, pour eviter
3023 // les melanges entre donnees SQL et variables plus aleatoires
3024 $variables_session = array('session_nom', 'session_email');
3025 foreach ($variables_session as $var) {
3026 if (_request($var) !== null) {
3032 #@spip_initialisation_suite();
3033 $session = charger_fonction('session', 'inc');
3035 include_spip('inc/texte');
3036 foreach ($variables_session as $var) {
3037 if (($a = _request($var)) !== null) {
3038 $GLOBALS['visiteur_session'][$var] = safehtml($a);
3041 if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
3042 $GLOBALS['visiteur_session']['id_auteur'] = 0;
3044 $session($GLOBALS['visiteur_session']);
3049 $h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
3050 if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
3052 $session = charger_fonction('session', 'inc');
3054 return $GLOBALS['visiteur_session']['statut'];
3056 if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
3057 include_spip('inc/auth');
3058 $h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
3061 $GLOBALS['visiteur_session'] = $h;
3063 return $GLOBALS['visiteur_session']['statut'];
3067 // au moins son navigateur nous dit la langue preferee de cet inconnu
3068 include_spip('inc/lang');
3069 utiliser_langue_visiteur();
3076 * Sélectionne la langue donnée en argument et mémorise la courante
3078 * Restaure l'ancienne langue si appellée sans argument.
3081 * On pourrait économiser l'empilement en cas de non changemnt
3082 * et lui faire retourner `False` pour prevenir l'appelant
3083 * Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3084 * cette fonction retourne toujours non `False`
3086 * @uses changer_langue()
3087 * @param null|string $lang
3088 * - string : Langue à appliquer,
3089 * - null : Pour restituer la dernière langue mémorisée.
3091 * - string Langue utilisée.
3093 function lang_select($lang = null) {
3094 static $pile_langues = array();
3095 if (!function_exists('changer_langue')) {
3096 include_spip('inc/lang');
3098 if ($lang === null) {
3099 $lang = array_pop($pile_langues);
3101 array_push($pile_langues, $GLOBALS['spip_lang']);
3103 if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3106 changer_langue($lang);
3112 * Renvoie une chaîne qui identifie la session courante
3114 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3115 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3118 * @pipeline_appel definir_session
3120 * @param bool $force
3122 * Identifiant de la session
3124 function spip_session($force = false) {
3126 if ($force or !isset($session)) {
3127 $s = pipeline('definir_session',
3128 $GLOBALS['visiteur_session']
3129 ?
serialize($GLOBALS['visiteur_session'])
3130 . '_' . @$_COOKIE['spip_session']
3133 $session = $s ?
substr(md5($s), 0, 8) : '';
3136 #spip_log('session: '.$session);
3142 * Retourne un lien vers une aide
3144 * Aide, aussi depuis l'espace privé à présent.
3145 * Surchargeable mais pas d'erreur fatale si indisponible.
3147 * @param string $aide
3148 * Cle d'identification de l'aide desiree
3149 * @param bool $distante
3150 * Generer une url locale (par defaut)
3151 * ou une url distante [directement sur spip.net]
3153 * Lien sur une icone d'aide
3155 function aider($aide = '', $distante = false) {
3156 $aider = charger_fonction('aide', 'inc', true);
3158 return $aider ?
$aider($aide, '', array(), $distante) : '';
3162 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3164 * Si l’utiliseur est un webmestre.
3166 function exec_info_dist() {
3168 include_spip('inc/autoriser');
3169 if (autoriser('webmestre')) {
3170 $cookies_masques = ['spip_session', 'PHPSESSID'];
3171 $cookies_backup = [];
3172 foreach ($cookies_masques as $k) {
3173 if (!empty($_COOKIE[$k])) {
3174 $cookies_backup[$k] = $_COOKIE[$k];
3175 $_COOKIE[$k] = '******************************';
3179 foreach ($cookies_backup as $k => $v) {
3183 include_spip('inc/filtres');
3184 sinon_interdire_acces();
3189 * Génère une erreur de squelette
3191 * Génère une erreur de squelette qui sera bien visible par un
3192 * administrateur authentifié lors d'une visite de la page en erreur
3194 * @param bool|string|array $message
3195 * - Message d'erreur (string|array)
3196 * - false pour retourner le texte des messages d'erreurs
3197 * - vide pour afficher les messages d'erreurs
3198 * @param string|array|object $lieu
3199 * Lieu d'origine de l'erreur
3200 * @return null|string
3201 * - Rien dans la plupart des cas
3202 * - string si $message à false.
3204 function erreur_squelette($message = '', $lieu = '') {
3205 $debusquer = charger_fonction('debusquer', 'public');
3206 if (is_array($lieu)) {
3207 include_spip('public/compiler');
3208 $lieu = reconstruire_contexte_compil($lieu);
3211 return $debusquer($message, $lieu);
3215 * Calcule un squelette avec un contexte et retourne son contenu
3217 * La fonction de base de SPIP : un squelette + un contexte => une page.
3218 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3219 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3220 * $options permet de selectionner les options suivantes :
3222 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3223 * - raw => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3224 * pour chaque $fond fourni.
3227 * @param string /array $fond
3228 * - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3229 * - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3230 * @param array $contexte
3231 * - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3232 * - La langue est transmise automatiquement (sauf option étoile).
3233 * @param array $options
3234 * Options complémentaires :
3236 * - trim : applique un trim sur le résultat (true par défaut)
3237 * - raw : retourne un tableau d'information sur le squelette (false par défaut)
3238 * - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3239 * équivalent de INCLURE*
3240 * - ajax : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3241 * @param string $connect
3242 * Non du connecteur de bdd a utiliser
3243 * @return string|array
3244 * - Contenu du squelette calculé
3245 * - ou tableau d'information sur le squelette.
3247 function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3248 if (!function_exists('evaluer_fond')) {
3249 include_spip('public/assembler');
3251 // assurer la compat avec l'ancienne syntaxe
3252 // (trim etait le 3eme argument, par defaut a true)
3253 if (!is_array($options)) {
3254 $options = array('trim' => $options);
3256 if (!isset($options['trim'])) {
3257 $options['trim'] = true;
3260 if (isset($contexte['connect'])) {
3261 $connect = $contexte['connect'];
3262 unset($contexte['connect']);
3268 if (!isset($options['etoile']) or !$options['etoile']) {
3269 // Si on a inclus sans fixer le critere de lang, on prend la langue courante
3270 if (!isset($contexte['lang'])) {
3271 $contexte['lang'] = $GLOBALS['spip_lang'];
3274 if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3275 $lang_select = lang_select($contexte['lang']);
3279 if (!isset($GLOBALS['_INC_PUBLIC'])) {
3280 $GLOBALS['_INC_PUBLIC'] = 0;
3283 $GLOBALS['_INC_PUBLIC']++
;
3286 foreach (is_array($fond) ?
$fond : array($fond) as $f) {
3287 $page = evaluer_fond($f, $contexte, $connect);
3289 $c = isset($options['compil']) ?
$options['compil'] : '';
3290 $a = array('fichier' => $fond);
3291 $erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3292 erreur_squelette($erreur, $c);
3293 // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3294 $page = array('texte' => '', 'erreur' => $erreur);
3297 $page = pipeline('recuperer_fond', array(
3298 'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3301 if (isset($options['ajax']) and $options['ajax']) {
3302 if (!function_exists('encoder_contexte_ajax')) {
3303 include_spip('inc/filtres');
3305 $page['texte'] = encoder_contexte_ajax(
3308 array('fond' => $f),
3309 ($connect ?
array('connect' => $connect) : array())
3317 if (isset($options['raw']) and $options['raw']) {
3320 $texte .= $options['trim'] ?
rtrim($page['texte']) : $page['texte'];
3324 $GLOBALS['_INC_PUBLIC']--;
3329 if (isset($options['raw']) and $options['raw']) {
3330 return is_array($fond) ?
$pages : reset($pages);
3332 return $options['trim'] ?
ltrim($texte) : $texte;
3337 * Trouve un squelette dans le repertoire modeles/
3342 function trouve_modele($nom) {
3343 return trouver_fond($nom, 'modeles/');
3347 * Trouver un squelette dans le chemin
3348 * on peut specifier un sous-dossier dans $dir
3349 * si $pathinfo est a true, retourne un tableau avec
3350 * les composantes du fichier trouve
3351 * + le chemin complet sans son extension dans fond
3353 * @param string $nom
3354 * @param string $dir
3355 * @param bool $pathinfo
3356 * @return array|string
3358 function trouver_fond($nom, $dir = '', $pathinfo = false) {
3359 $f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES
, $dir ?
rtrim($dir, '/') . '/' : '');
3363 // renvoyer un tableau detaille si $pathinfo==true
3365 if (!isset($p['extension']) or !$p['extension']) {
3366 $p['extension'] = _EXTENSION_SQUELETTES
;
3368 if (!isset($p['extension']) or !$p['filename']) {
3369 $p['filename'] = ($p['basename'] ?
substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3371 $p['fond'] = ($f ?
substr($f, 0, -strlen($p['extension']) - 1) : '');
3377 * Teste, pour un nom de page de l'espace privé, s'il est possible
3378 * de générer son contenu.
3380 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3381 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3382 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3384 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3385 * ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3386 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3388 * @param string $nom
3391 * Nom de l'exec, sinon chaîne vide.
3393 function tester_url_ecrire($nom) {
3394 static $exec = array();
3395 if (isset($exec[$nom])) {
3398 // tester si c'est une page en squelette
3399 if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3400 return $exec[$nom] = 'fond';
3401 } // compat skels orthogonaux version precedente
3402 elseif (trouver_fond($nom, 'prive/exec/')) {
3403 return $exec[$nom] = 'fond_monobloc';
3404 } // echafaudage d'un fond !
3405 elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3406 return $exec[$nom] = 'fond';
3408 // attention, il ne faut pas inclure l'exec ici
3409 // car sinon #URL_ECRIRE provoque des inclusions
3410 // et des define intrusifs potentiels
3411 return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ?
$nom : '');
3416 * Tente de charger dynamiquement une extension PHP
3420 * $ok = charger_php_extension('sqlite');
3422 * @uses inc_charger_php_extension_dist() Si la librairie n'est pas déjà charchée
3424 * @param string $module Nom du module à charger
3425 * @return bool true si le module est chargé
3427 function charger_php_extension($module) {
3428 if (extension_loaded($module)) {
3431 $charger_php_extension = charger_fonction('charger_php_extension', 'inc');
3433 return $charger_php_extension($module);
3439 * Indique si le code HTML5 est permis sur le site public
3442 * true si et seulement si la configuration autorise le code HTML5 sur le site public
3444 function html5_permis() {
3445 return (isset($GLOBALS['meta']['version_html_max'])
3446 and ('html5' == $GLOBALS['meta']['version_html_max']));
3450 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3451 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3452 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3456 * lire_meta : fonction dépréciée
3458 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3459 * @see lire_config()
3460 * @param string $nom Clé de meta à lire
3461 * @return mixed Valeur de la meta.
3463 function lire_meta($nom) {
3464 return isset($GLOBALS['meta'][$nom]) ?
$GLOBALS['meta'][$nom] : null;
3469 * ecrire_metas : fonction dépréciée
3473 function ecrire_metas() { }
3476 * Retourne une ligne d'un résultat de requête mysql (déprécié)
3479 * @deprecated Utiliser sql_fetch()
3480 * @param Ressource $r Ressource mysql
3481 * @param int|null $t Type de retour
3482 * @return array|void|bool Tableau de la ligne SQL
3484 function spip_fetch_array($r, $t = null) {
3487 return sql_fetch($r);
3490 if ($t == 'SPIP_NUM') {
3493 if ($t == 'SPIP_BOTH') {
3496 if ($t == 'SPIP_ASSOC') {
3499 spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
3501 return mysqli_fetch_array($r, $t);
3507 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3508 * au prochain passage dans l'espace prive
3509 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3510 * les alertes affichees une fois sont effacees
3512 * @param string $nom
3513 * @param string $message
3514 * @param string $statut
3516 function avertir_auteurs($nom, $message, $statut = '') {
3517 $alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3519 or !is_array($alertes = unserialize($alertes))
3524 if (!isset($alertes[$statut])) {
3525 $alertes[$statut] = array();
3527 $alertes[$statut][$nom] = $message;
3528 ecrire_meta("message_alertes_auteurs", serialize($alertes));
3531 if (PHP_VERSION_ID
< 50500) {
3532 if (!function_exists('array_column')) {
3534 * Returns the values from a single column of the input array, identified by
3537 * Optionally, you may provide an $indexKey to index the values in the returned
3538 * array by the values from the $indexKey column in the input array.
3540 * @link http://php.net/manual/fr/function.array-column.php
3541 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3542 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3543 * @license http://opensource.org/licenses/MIT MIT
3545 * @param array $input A multi-dimensional array (record set) from which to pull
3546 * a column of values.
3547 * @param mixed $columnKey The column of values to return. This value may be the
3548 * integer key of the column you wish to retrieve, or it
3549 * may be the string key name for an associative array.
3550 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3551 * the returned array. This value may be the integer key
3552 * of the column, or it may be the string key name.
3555 function array_column($input = null, $columnKey = null, $indexKey = null)
3557 // Using func_get_args() in order to check for proper number of
3558 // parameters and trigger errors exactly as the built-in array_column()
3560 $argc = func_num_args();
3561 $params = func_get_args();
3564 trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING
);
3568 if (!is_array($params[0])) {
3570 'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3576 if (!is_int($params[1])
3577 && !is_float($params[1])
3578 && !is_string($params[1])
3579 && $params[1] !== null
3580 && !(is_object($params[1]) && method_exists($params[1], '__toString'))
3582 trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING
);
3586 if (isset($params[2])
3587 && !is_int($params[2])
3588 && !is_float($params[2])
3589 && !is_string($params[2])
3590 && !(is_object($params[2]) && method_exists($params[2], '__toString'))
3592 trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING
);
3596 $paramsInput = $params[0];
3597 $paramsColumnKey = ($params[1] !== null) ?
(string) $params[1] : null;
3599 $paramsIndexKey = null;
3600 if (isset($params[2])) {
3601 if (is_float($params[2]) ||
is_int($params[2])) {
3602 $paramsIndexKey = (int) $params[2];
3604 $paramsIndexKey = (string) $params[2];
3608 $resultArray = array();
3610 foreach ($paramsInput as $row) {
3611 $key = $value = null;
3612 $keySet = $valueSet = false;
3614 if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3616 $key = (string) $row[$paramsIndexKey];
3619 if ($paramsColumnKey === null) {
3622 } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3624 $value = $row[$paramsColumnKey];
3629 $resultArray[$key] = $value;
3631 $resultArray[] = $value;
3637 return $resultArray;
3644 * Nettoie une chaine pour servir comme classes CSS.
3647 * les classes CSS acceptent théoriquement tous les caractères sauf NUL.
3648 * Ici, on limite (enlève) les caractères autres qu’alphanumérique, espace, - + _ @
3650 * @param string|string[] $classes
3651 * @return string|string[]
3653 function spip_sanitize_classname($classes) {
3654 if (is_array($classes)) {
3655 return array_map('spip_sanitize_classname', $classes);
3657 return preg_replace("/[^ 0-9a-z_\-+@]/i", "", $classes);