a89658a6a29ac87920708561fd21e3963832b8e4
[lhc/web/www.git] / www / ecrire / inc / utils.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Utilitaires indispensables autour du serveur Http.
15 *
16 * @package SPIP\Core\Utilitaires
17 **/
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23
24 /**
25 * Cherche une fonction surchargeable et en retourne le nom exact,
26 * après avoir chargé le fichier la contenant si nécessaire.
27 *
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`
30 *
31 * Peut être appelé plusieurs fois, donc optimisé.
32 *
33 * @api
34 * @uses include_spip() Pour charger le fichier
35 * @example
36 * ```
37 * $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
38 * $envoyer_mail($email, $sujet, $texte);
39 * ```
40 *
41 * @param string $nom
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
47 * @return string
48 * Nom de la fonction, ou false.
49 */
50 function charger_fonction($nom, $dossier = 'exec', $continue = false) {
51 static $echecs = array();
52
53 if (strlen($dossier) and substr($dossier, -1) != '/') {
54 $dossier .= '/';
55 }
56 $f = str_replace('/', '_', $dossier) . $nom;
57
58 if (function_exists($f)) {
59 return $f;
60 }
61 if (function_exists($g = $f . '_dist')) {
62 return $g;
63 }
64
65 if (isset($echecs[$f])) {
66 return $echecs[$f];
67 }
68 // Sinon charger le fichier de declaration si plausible
69
70 if (!preg_match(',^\w+$,', $f)) {
71 if ($continue) {
72 return false;
73 } //appel interne, on passe
74 include_spip('inc/minipres');
75 echo minipres();
76 exit;
77 }
78
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) != '.'
85 ) {
86 include_spip(substr($dossier, 0, -1));
87 }
88 if (function_exists($f)) {
89 return $f;
90 }
91 if (function_exists($g)) {
92 return $g;
93 }
94
95 if ($continue) {
96 return $echecs[$f] = false;
97 }
98
99 // Echec : message d'erreur
100 spip_log("fonction $nom ($f ou $g) indisponible" .
101 ($inc ? "" : " (fichier $d absent de $dossier)"));
102
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));
107 exit;
108 }
109
110 /**
111 * Inclusion unique avec verification d'existence du fichier + log en crash sinon
112 *
113 * @param string $file
114 * @return bool
115 */
116 function include_once_check($file) {
117 if (file_exists($file)) {
118 include_once $file;
119
120 return true;
121 }
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));
126
127 return false;
128 }
129
130
131 /**
132 * Inclut un fichier PHP (en le cherchant dans les chemins)
133 *
134 * @api
135 * @uses find_in_path()
136 * @example
137 * ```
138 * include_spip('inc/texte');
139 * ```
140 *
141 * @param string $f
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é
149 **/
150 function include_spip($f, $include = true) {
151 return find_in_path($f . '.php', '', $include);
152 }
153
154 /**
155 * Requiert un fichier PHP (en le cherchant dans les chemins)
156 *
157 * @uses find_in_path()
158 * @see include_spip()
159 * @example
160 * ```
161 * require_spip('inc/texte');
162 * ```
163 *
164 * @param string $f
165 * Nom du fichier (sans l'extension)
166 * @return string|bool
167 * - false : fichier introuvable
168 * - string : chemin du fichier trouvé
169 **/
170 function require_spip($f) {
171 return find_in_path($f . '.php', '', 'required');
172 }
173
174 /**
175 * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
176 *
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
181 *
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
186 *
187 * on passe $val par reference pour limiter les allocations memoire
188 *
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
195 **/
196 function minipipe($fonc, &$val) {
197 // fonction
198 if (function_exists($fonc)) {
199 $val = call_user_func($fonc, $val);
200 } // Class::Methode
201 else {
202 if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
203 and $methode = array($regs[1], $regs[2])
204 and is_callable($methode)
205 ) {
206 $val = call_user_func($methode, $val);
207 } else {
208 spip_log("Erreur - '$fonc' non definie !");
209 }
210 }
211
212 return $val;
213 }
214
215 /**
216 * Appel d’un pipeline
217 *
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.
221 *
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`.
224 *
225 *
226 * @example
227 * Appel du pipeline `pre_insertion`
228 * ```
229 * $champs = pipeline('pre_insertion', array(
230 * 'args' => array('table' => 'spip_articles'),
231 * 'data' => $champs
232 * ));
233 * ```
234 *
235 * @param string $action
236 * Nom du pipeline
237 * @param null|string|array $val
238 * Données à l’entrée du pipeline
239 * @return mixed|null
240 * Résultat
241 */
242 function pipeline($action, $val = null) {
243 static $charger;
244
245 // chargement initial des fonctions mises en cache, ou generation du cache
246 if (!$charger) {
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");
254 }
255 }
256
257 if ($ok) {
258 include_once $charger;
259 }
260 }
261
262 // appliquer notre fonction si elle existe
263 $fonc = 'execute_pipeline_' . strtolower($action);
264 if (function_exists($fonc)) {
265 $val = $fonc($val);
266 } // plantage ?
267 else {
268 spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
269 }
270
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
274 if (is_array($val)
275 and count($val) == 2
276 and (array_key_exists('data', $val))
277 ) {
278 $val = $val['data'];
279 }
280
281 return $val;
282 }
283
284 /**
285 * Enregistrement des événements
286 *
287 * Signature : `spip_log(message[,niveau|type|type.niveau])`
288 *
289 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
290 *
291 * Les différents niveaux possibles sont :
292 *
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'
301 *
302 * @example
303 * ```
304 * spip_log($message)
305 * spip_log($message, 'recherche')
306 * spip_log($message, _LOG_DEBUG)
307 * spip_log($message, 'recherche.'._LOG_DEBUG)
308 * ```
309 *
310 * @api
311 * @link http://programmer.spip.net/spip_log
312 * @uses inc_log_dist()
313 *
314 * @param string $message
315 * Message à loger
316 * @param string|int $name
317 *
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.
323 */
324 function spip_log($message = null, $name = null) {
325 static $pre = array();
326 static $log;
327 preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
328 if (!isset($regs[1]) or !$logname = $regs[1]) {
329 $logname = null;
330 }
331 if (!isset($regs[2]) or !$niveau = $regs[2]) {
332 $niveau = _LOG_INFO;
333 }
334
335 if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
336 if (!$pre) {
337 $pre = array(
338 _LOG_HS => 'HS:',
339 _LOG_ALERTE_ROUGE => 'ALERTE:',
340 _LOG_CRITIQUE => 'CRITIQUE:',
341 _LOG_ERREUR => 'ERREUR:',
342 _LOG_AVERTISSEMENT => 'WARNING:',
343 _LOG_INFO_IMPORTANTE => '!INFO:',
344 _LOG_INFO => 'info:',
345 _LOG_DEBUG => 'debug:'
346 );
347 $log = charger_fonction('log', 'inc');
348 }
349 if (!is_string($message)) {
350 $message = print_r($message, true);
351 }
352 $log($pre[$niveau] . ' ' . $message, $logname);
353 }
354 }
355
356 /**
357 * Enregistrement des journaux
358 *
359 * @uses inc_journal_dist()
360 * @param string $phrase Texte du journal
361 * @param array $opt Tableau d'options
362 **/
363 function journal($phrase, $opt = array()) {
364 $journal = charger_fonction('journal', 'inc');
365 $journal($phrase, $opt);
366 }
367
368
369 /**
370 * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
371 * ou pioché dans un tableau transmis
372 *
373 * @api
374 * @param string $var
375 * Clé souhaitée
376 * @param bool|array $c
377 * Tableau transmis (sinon cherche dans GET ou POST)
378 * @return mixed|null
379 * - null si la clé n'a pas été trouvée
380 * - la valeur de la clé sinon.
381 **/
382 function _request($var, $c = false) {
383
384 if (is_array($c)) {
385 return isset($c[$var]) ? $c[$var] : null;
386 }
387
388 if (isset($_GET[$var])) {
389 $a = $_GET[$var];
390 } elseif (isset($_POST[$var])) {
391 $a = $_POST[$var];
392 } else {
393 return null;
394 }
395
396 // Si on est en ajax et en POST tout a ete encode
397 // via encodeURIComponent, il faut donc repasser
398 // dans le charset local...
399 if (defined('_AJAX')
400 and _AJAX
401 and isset($GLOBALS['meta']['charset'])
402 and $GLOBALS['meta']['charset'] != 'utf-8'
403 and is_string($a)
404 // check rapide mais pas fiable
405 and preg_match(',[\x80-\xFF],', $a)
406 // check fiable
407 and include_spip('inc/charsets')
408 and is_utf8($a)
409 ) {
410 return importer_charset($a, 'utf-8');
411 }
412
413 return $a;
414 }
415
416
417 /**
418 * Affecte une valeur à une clé (pour usage avec `_request()`)
419 *
420 * @see _request() Pour obtenir la valeur
421 * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
422 *
423 * @param string $var Nom de la clé
424 * @param string $val Valeur à affecter
425 * @param bool|array $c Tableu de données (sinon utilise `$_GET` et `$_POST`)
426 * @return array|bool
427 * - array $c complété si un $c est transmis,
428 * - false sinon
429 **/
430 function set_request($var, $val = null, $c = false) {
431 if (is_array($c)) {
432 unset($c[$var]);
433 if ($val !== null) {
434 $c[$var] = $val;
435 }
436
437 return $c;
438 }
439
440 unset($_GET[$var]);
441 unset($_POST[$var]);
442 if ($val !== null) {
443 $_GET[$var] = $val;
444 }
445
446 return false; # n'affecte pas $c
447 }
448
449 /**
450 * Sanitizer une valeur *SI* elle provient du GET ou POST
451 * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
452 * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
453 * mais qui doit etre nettoyee si elle provient de l'URL
454 *
455 * On peut sanitizer
456 * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
457 * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
458 * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
459 *
460 * @param string|array $value
461 * @param string|array $key
462 * @param string $sanitize_function
463 * @return array|mixed|string
464 */
465 function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
466 if (is_array($value)) {
467 if ($key=='*') {
468 $key = array_keys($value);
469 }
470 if (!is_array($key)) {
471 $key = [$key];
472 }
473 foreach ($key as $k) {
474 if (!empty($value[$k])) {
475 $value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
476 }
477 }
478 return $value;
479 }
480 // si la valeur vient des GET ou POST on la sanitize
481 if (!empty($value) and $value == _request($key)) {
482 $value = $sanitize_function($value);
483 }
484 return $value;
485 }
486
487 /**
488 * Tester si une URL est absolue
489 *
490 * On est sur le web, on exclut certains protocoles,
491 * notamment 'file://', 'php://' et d'autres…
492
493 * @param string $url
494 * @return bool
495 */
496 function tester_url_absolue($url) {
497 $url = trim($url);
498 if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
499 if (
500 isset($m[1])
501 and $p = strtolower(rtrim($m[1], ':'))
502 and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
503 ) {
504 return false;
505 }
506 return true;
507 }
508 return false;
509 }
510
511 /**
512 * Prend une URL et lui ajoute/retire un paramètre
513 *
514 * @filtre
515 * @link http://www.spip.net/4255
516 * @example
517 * ```
518 * [(#SELF|parametre_url{suite,18})] (ajout)
519 * [(#SELF|parametre_url{suite,''})] (supprime)
520 * [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
521 * ```
522 *
523 * @param string $url URL
524 * @param string $c Nom du paramètre
525 * @param string|array|null $v Valeur du paramètre
526 * @param string $sep Séparateur entre les paramètres
527 * @return string URL
528 */
529 function parametre_url($url, $c, $v = null, $sep = '&amp;') {
530 // requete erronnee : plusieurs variable dans $c et aucun $v
531 if (strpos($c, "|") !== false and is_null($v)) {
532 return null;
533 }
534
535 // lever l'#ancre
536 if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
537 $url = $r[1];
538 $ancre = $r[2];
539 } else {
540 $ancre = '';
541 }
542
543 // eclater
544 $url = preg_split(',[?]|&amp;|&,', $url);
545
546 // recuperer la base
547 $a = array_shift($url);
548 if (!$a) {
549 $a = './';
550 }
551
552 $regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
553 $ajouts = array_flip(explode('|', $c));
554 $u = is_array($v) ? $v : rawurlencode($v);
555 $testv = (is_array($v) ? count($v) : strlen($v));
556 $v_read = null;
557 // lire les variables et agir
558 foreach ($url as $n => $val) {
559 if (preg_match($regexp, urldecode($val), $r)) {
560 $r = array_pad($r, 3, null);
561 if ($v === null) {
562 // c'est un tableau, on memorise les valeurs
563 if (substr($r[1], -2) == "[]") {
564 if (!$v_read) {
565 $v_read = array();
566 }
567 $v_read[] = $r[2] ? substr($r[2], 1) : '';
568 } // c'est un scalaire, on retourne direct
569 else {
570 return $r[2] ? substr($r[2], 1) : '';
571 }
572 } // suppression
573 elseif (!$testv) {
574 unset($url[$n]);
575 }
576 // Ajout. Pour une variable, remplacer au meme endroit,
577 // pour un tableau ce sera fait dans la prochaine boucle
578 elseif (substr($r[1], -2) != '[]') {
579 $url[$n] = $r[1] . '=' . $u;
580 unset($ajouts[$r[1]]);
581 }
582 // Pour les tableaux on laisse tomber les valeurs de
583 // départ, on remplira à l'étape suivante
584 else {
585 unset($url[$n]);
586 }
587 }
588 }
589
590 // traiter les parametres pas encore trouves
591 if ($v === null
592 and $args = func_get_args()
593 and count($args) == 2
594 ) {
595 return $v_read; // rien trouve ou un tableau
596 } elseif ($testv) {
597 foreach ($ajouts as $k => $n) {
598 if (!is_array($v)) {
599 $url[] = $k . '=' . $u;
600 } else {
601 $id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
602 foreach ($v as $w) {
603 $url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
604 }
605 }
606 }
607 }
608
609 // eliminer les vides
610 $url = array_filter($url);
611
612 // recomposer l'adresse
613 if ($url) {
614 $a .= '?' . join($sep, $url);
615 }
616
617 return $a . $ancre;
618 }
619
620 /**
621 * Ajoute (ou retire) une ancre sur une URL
622 *
623 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
624 * et on remplace ceux à l'interieur ou au bout par `-`
625 *
626 * @example
627 * - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
628 * - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
629 * @uses translitteration()
630 * @param string $url
631 * @param string $ancre
632 * @return string
633 */
634 function ancre_url($url, $ancre) {
635 // lever l'#ancre
636 if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
637 $url = $r[1];
638 }
639 if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
640 if (!function_exists('translitteration')) {
641 include_spip('inc/charsets');
642 }
643 $ancre = preg_replace(
644 array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
645 array('', '-'),
646 translitteration($ancre)
647 );
648 }
649 return $url . (strlen($ancre) ? '#' . $ancre : '');
650 }
651
652 /**
653 * Pour le nom du cache, les `types_urls` et `self`
654 *
655 * @param string|null $reset
656 * @return string
657 */
658 function nettoyer_uri($reset = null) {
659 static $done = false;
660 static $propre = '';
661 if (!is_null($reset)) {
662 return $propre = $reset;
663 }
664 if ($done) {
665 return $propre;
666 }
667 $done = true;
668 return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
669 }
670
671 /**
672 * Nettoie une request_uri des paramètres var_xxx
673 * @param $request_uri
674 * @return string
675 */
676 function nettoyer_uri_var($request_uri) {
677 $uri1 = $request_uri;
678 do {
679 $uri = $uri1;
680 $uri1 = preg_replace(',([?&])(PHPSESSID|(var_[^=&]*))=[^&]*(&|$),i',
681 '\1', $uri);
682 } while ($uri <> $uri1);
683 return preg_replace(',[?&]$,', '', $uri1);
684 }
685
686
687 /**
688 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
689 *
690 * @param string $amp
691 * Style des esperluettes
692 * @param bool $root
693 * @return string
694 * URL vers soi-même
695 **/
696 function self($amp = '&amp;', $root = false) {
697 $url = nettoyer_uri();
698 if (!$root
699 and (
700 // si pas de profondeur on peut tronquer
701 $GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
702 // sinon c'est OK si _SET_HTML_BASE a ete force a false
703 or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
704 ) {
705 $url = preg_replace(',^[^?]*/,', '', $url);
706 }
707 // ajouter le cas echeant les variables _POST['id_...']
708 foreach ($_POST as $v => $c) {
709 if (substr($v, 0, 3) == 'id_') {
710 $url = parametre_url($url, $v, $c, '&');
711 }
712 }
713
714 // supprimer les variables sans interet
715 if (test_espace_prive()) {
716 $url = preg_replace(',([?&])('
717 . 'lang|show_docs|'
718 . 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
719 $url = preg_replace(',([?&])[&]+,', '\1', $url);
720 $url = preg_replace(',[&]$,', '\1', $url);
721 }
722
723 // eviter les hacks
724 include_spip('inc/filtres_mini');
725 $url = spip_htmlspecialchars($url);
726
727 $url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
728
729 // &amp; ?
730 if ($amp != '&amp;') {
731 $url = str_replace('&amp;', $amp, $url);
732 }
733
734 // Si ca demarre par ? ou vide, donner './'
735 $url = preg_replace(',^([?].*)?$,', './\1', $url);
736
737 return $url;
738 }
739
740
741 /**
742 * Indique si on est dans l'espace prive
743 *
744 * @return bool
745 * true si c'est le cas, false sinon.
746 */
747 function test_espace_prive() {
748 return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
749 }
750
751 /**
752 * Vérifie la présence d'un plugin actif, identifié par son préfixe
753 *
754 * @param string $plugin
755 * @return bool
756 */
757 function test_plugin_actif($plugin) {
758 return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
759 }
760
761 /**
762 * Traduction des textes de SPIP
763 *
764 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
765 *
766 * @api
767 * @uses inc_traduire_dist()
768 * @uses _L()
769 * @example
770 * ```
771 * _T('bouton_enregistrer')
772 * _T('medias:image_tourner_droite')
773 * _T('medias:erreurs', array('nb'=>3))
774 * _T("email_sujet", array('spip_lang'=>$lang_usager))
775 * ```
776 *
777 * @param string $texte
778 * Clé de traduction
779 * @param array $args
780 * Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
781 * @param array $options
782 * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
783 * - bool force : forcer un retour meme si la chaine n'a pas de traduction
784 * - bool sanitize : nettoyer le html suspect dans les arguments
785 * @return string
786 * Texte
787 */
788 function _T($texte, $args = array(), $options = array()) {
789 static $traduire = false;
790 $o = array('class' => '', 'force' => true, 'sanitize' => true);
791 if ($options) {
792 // support de l'ancien argument $class
793 if (is_string($options)) {
794 $options = array('class' => $options);
795 }
796 $o = array_merge($o, $options);
797 }
798
799 if (!$traduire) {
800 $traduire = charger_fonction('traduire', 'inc');
801 include_spip('inc/lang');
802 }
803
804 // On peut passer explicitement la langue dans le tableau
805 // On utilise le même nom de variable que la globale
806 if (isset($args['spip_lang'])) {
807 $lang = $args['spip_lang'];
808 // On l'enleve pour ne pas le passer au remplacement
809 unset($args['spip_lang']);
810 } // Sinon on prend la langue du contexte
811 else {
812 $lang = $GLOBALS['spip_lang'];
813 }
814 $text = $traduire($texte, $lang);
815
816 if (!strlen($text)) {
817 if (!$o['force']) {
818 return '';
819 }
820
821 $text = $texte;
822
823 // pour les chaines non traduites, assurer un service minimum
824 if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
825 $text = str_replace('_', ' ',
826 (($n = strpos($text, ':')) === false ? $texte :
827 substr($texte, $n + 1)));
828 }
829 $o['class'] = null;
830
831 }
832
833 return _L($text, $args, $o);
834
835 }
836
837
838 /**
839 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
840 *
841 * Cette fonction est également appelée dans le code source de SPIP quand une
842 * chaîne n'est pas encore dans les fichiers de langue.
843 *
844 * @see _T()
845 * @example
846 * ```
847 * _L('Texte avec @nb@ ...', array('nb'=>3)
848 * ```
849 *
850 * @param string $text
851 * Texte
852 * @param array $args
853 * Couples (variable => valeur) à transformer dans le texte
854 * @param array $options
855 * - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
856 * - bool sanitize : nettoyer le html suspect dans les arguments
857 * @return string
858 * Texte
859 */
860 function _L($text, $args = array(), $options = array()) {
861 $f = $text;
862 $defaut_options = array(
863 'class' => null,
864 'sanitize' => true,
865 );
866 // support de l'ancien argument $class
867 if ($options and is_string($options)) {
868 $options = array('class' => $options);
869 }
870 if (is_array($options)) {
871 $options += $defaut_options;
872 } else {
873 $options = $defaut_options;
874 }
875
876 if (is_array($args) and count($args)) {
877 if (!function_exists('interdire_scripts')) {
878 include_spip('inc/texte');
879 }
880 if (!function_exists('echapper_html_suspect')) {
881 include_spip('inc/texte_mini');
882 }
883 foreach ($args as $name => $value) {
884 if ($options['sanitize']) {
885 $value = echapper_html_suspect($value);
886 $value = interdire_scripts($value, -1);
887 }
888 if (!empty($options['class'])) {
889 $value = "<span class='".$options['class']."'>$value</span>";
890 }
891 $t = str_replace("@$name@", $value, $text);
892 if ($text !== $t) {
893 unset($args[$name]);
894 $text = $t;
895 }
896 }
897 // Si des variables n'ont pas ete inserees, le signaler
898 // (chaines de langues pas a jour)
899 if ($args) {
900 spip_log("$f: variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
901 }
902 }
903
904 if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
905 return "<span class=debug-traduction-erreur>$text</span>";
906 } else {
907 return $text;
908 }
909 }
910
911
912 /**
913 * Retourne un joli chemin de répertoire
914 *
915 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
916 * ou `tmp/` au lieu de `../tmp/`
917 *
918 * @param stirng $rep Chemin d’un répertoire
919 * @return string
920 */
921 function joli_repertoire($rep) {
922 $a = substr($rep, 0, 1);
923 if ($a <> '.' and $a <> '/') {
924 $rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
925 }
926 $rep = preg_replace(',(^\.\.\/),', '', $rep);
927
928 return $rep;
929 }
930
931
932 /**
933 * Débute ou arrête un chronomètre et retourne sa valeur
934 *
935 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
936 * la seconde fois pour l’arrêter et récupérer la valeur
937 *
938 * @example
939 * ```
940 * spip_timer('papoter');
941 * // actions
942 * $duree = spip_timer('papoter');
943 * ```
944 *
945 * @param string $t
946 * Nom du chronomètre
947 * @param bool $raw
948 * - false : retour en texte humainement lisible
949 * - true : retour en millisecondes
950 * @return float|int|string|void
951 */
952 function spip_timer($t = 'rien', $raw = false) {
953 static $time;
954 $a = time();
955 $b = microtime();
956 // microtime peut contenir les microsecondes et le temps
957 $b = explode(' ', $b);
958 if (count($b) == 2) {
959 $a = end($b);
960 } // plus precis !
961 $b = reset($b);
962 if (!isset($time[$t])) {
963 $time[$t] = $a + $b;
964 } else {
965 $p = ($a + $b - $time[$t]) * 1000;
966 unset($time[$t]);
967 # echo "'$p'";exit;
968 if ($raw) {
969 return $p;
970 }
971 if ($p < 1000) {
972 $s = '';
973 } else {
974 $s = sprintf("%d ", $x = floor($p / 1000));
975 $p -= ($x * 1000);
976 }
977
978 return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
979 }
980 }
981
982
983 // Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
984 // sinon renvoie True et le date sauf si ca n'est pas souhaite
985 // http://code.spip.net/@spip_touch
986 function spip_touch($fichier, $duree = 0, $touch = true) {
987 if ($duree) {
988 clearstatcache();
989 if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
990 return false;
991 }
992 }
993 if ($touch !== false) {
994 if (!@touch($fichier)) {
995 spip_unlink($fichier);
996 @touch($fichier);
997 };
998 @chmod($fichier, _SPIP_CHMOD & ~0111);
999 }
1000
1001 return true;
1002 }
1003
1004
1005 /**
1006 * Action qui déclenche une tache de fond
1007 *
1008 * @see queue_affichage_cron()
1009 * @see action_super_cron_dist()
1010 * @uses cron()
1011 **/
1012 function action_cron() {
1013 include_spip('inc/headers');
1014 http_status(204); // No Content
1015 header("Connection: close");
1016 define('_DIRECT_CRON_FORCE', true);
1017 cron();
1018 }
1019
1020 /**
1021 * Exécution des tâches de fond
1022 *
1023 * @uses inc_genie_dist()
1024 *
1025 * @param array $taches
1026 * Tâches forcées
1027 * @param array $taches_old
1028 * Tâches forcées, pour compat avec ancienne syntaxe
1029 * @return bool
1030 * True si la tache a pu être effectuée
1031 */
1032 function cron($taches = array(), $taches_old = array()) {
1033 // si pas en mode cron force, laisser tomber.
1034 if (!defined('_DIRECT_CRON_FORCE')) {
1035 return false;
1036 }
1037 if (!is_array($taches)) {
1038 $taches = $taches_old;
1039 } // compat anciens appels
1040 // si taches a inserer en base et base inaccessible, laisser tomber
1041 // sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1042 // queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1043 // et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1044 if ($taches and count($taches) and !spip_connect()) {
1045 return false;
1046 }
1047 spip_log("cron !", 'jq' . _LOG_DEBUG);
1048 if ($genie = charger_fonction('genie', 'inc', true)) {
1049 return $genie($taches);
1050 }
1051
1052 return false;
1053 }
1054
1055 /**
1056 * Ajout d'une tache dans la file d'attente
1057 *
1058 * @param string $function
1059 * Le nom de la fonction PHP qui doit être appelée.
1060 * @param string $description
1061 * Une description humainement compréhensible de ce que fait la tâche
1062 * (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1063 * @param array $arguments
1064 * Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1065 * @param string $file
1066 * Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1067 * exemple : `'inc/mail'` : il ne faut pas indiquer .php
1068 * Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1069 * @param bool $no_duplicate
1070 * Facultatif, `false` par défaut
1071 *
1072 * - 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.
1073 * - 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
1074 * @param int $time
1075 * Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1076 * 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).
1077 * @param int $priority
1078 * Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1079 * 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.
1080 * @return int
1081 * Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1082 */
1083 function job_queue_add(
1084 $function,
1085 $description,
1086 $arguments = array(),
1087 $file = '',
1088 $no_duplicate = false,
1089 $time = 0,
1090 $priority = 0
1091 ) {
1092 include_spip('inc/queue');
1093
1094 return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1095 }
1096
1097 /**
1098 * Supprimer une tache de la file d'attente
1099 *
1100 * @param int $id_job
1101 * id of jonb to delete
1102 * @return bool
1103 */
1104 function job_queue_remove($id_job) {
1105 include_spip('inc/queue');
1106
1107 return queue_remove_job($id_job);
1108 }
1109
1110 /**
1111 * Associer une tache a un/des objets de SPIP
1112 *
1113 * @param int $id_job
1114 * id of job to link
1115 * @param array $objets
1116 * can be a simple array('objet'=>'article', 'id_objet'=>23)
1117 * or an array of simple array to link multiples objet in one time
1118 */
1119 function job_queue_link($id_job, $objets) {
1120 include_spip('inc/queue');
1121
1122 return queue_link_job($id_job, $objets);
1123 }
1124
1125
1126 /**
1127 * Renvoyer le temps de repos restant jusqu'au prochain job
1128 *
1129 * @staticvar int $queue_next_job_time
1130 * @see queue_set_next_job_time()
1131 * @param int|bool $force
1132 * Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1133 *
1134 * - si `true`, force la relecture depuis le fichier
1135 * - si int, affecte la static directement avec la valeur
1136 * @return int
1137 *
1138 * - `0` si un job est à traiter
1139 * - `null` si la queue n'est pas encore initialisée
1140 */
1141 function queue_sleep_time_to_next_job($force = null) {
1142 static $queue_next_job_time = -1;
1143 if ($force === true) {
1144 $queue_next_job_time = -1;
1145 } elseif ($force) {
1146 $queue_next_job_time = $force;
1147 }
1148
1149 if ($queue_next_job_time == -1) {
1150 if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1151 define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1152 }
1153 // utiliser un cache memoire si dispo
1154 if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1155 $queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1156 } else {
1157 $queue_next_job_time = null;
1158 if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1159 $queue_next_job_time = intval($contenu);
1160 }
1161 }
1162 }
1163
1164 if (is_null($queue_next_job_time)) {
1165 return null;
1166 }
1167 if (!$_SERVER['REQUEST_TIME']) {
1168 $_SERVER['REQUEST_TIME'] = time();
1169 }
1170
1171 return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1172 }
1173
1174
1175 /**
1176 * Transformation XML des `&` en `&amp;`
1177 *
1178 * @pipeline post_typo
1179 * @param string $u
1180 * @return string
1181 */
1182 function quote_amp($u) {
1183 return preg_replace(
1184 "/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1185 "&amp;", $u);
1186 }
1187
1188
1189 /**
1190 * Produit une balise `<script>` valide
1191 *
1192 * @example
1193 * ```
1194 * echo http_script('alert("ok");');
1195 * echo http_script('','js/jquery.js');
1196 * ```
1197 *
1198 * @param string $script
1199 * Code source du script
1200 * @param string $src
1201 * Permet de faire appel à un fichier javascript distant
1202 * @param string $noscript
1203 * Contenu de la balise `<noscript>`
1204 * @return string
1205 * Balise HTML `<script>` et son contenu
1206 **/
1207 function http_script($script, $src = '', $noscript = '') {
1208 static $done = array();
1209
1210 if ($src && !isset($done[$src])) {
1211 $done[$src] = true;
1212 $src = find_in_path($src, _JAVASCRIPT);
1213 $src = " src='$src'";
1214 } else {
1215 $src = '';
1216 }
1217 if ($script) {
1218 $script = ("/*<![CDATA[*/\n" .
1219 preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1220 "/*]]>*/");
1221 }
1222 if ($noscript) {
1223 $noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1224 }
1225
1226 return ($src or $script or $noscript)
1227 ? "<script type='text/javascript'$src>$script</script>$noscript"
1228 : '';
1229 }
1230
1231
1232 /**
1233 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1234 *
1235 * Transforme n'importe quel texte en une chaîne utilisable
1236 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1237 * simples (`'` uniquement ; pas `"`)
1238 *
1239 * Utile particulièrement en filtre dans un squelettes
1240 * pour écrire un contenu dans une variable JS ou PHP.
1241 *
1242 * Échappe les apostrophes (') du contenu transmis.
1243 *
1244 * @link http://www.spip.net/4281
1245 * @example
1246 * PHP dans un squelette
1247 * ```
1248 * $x = '[(#TEXTE|texte_script)]';
1249 * ```
1250 *
1251 * JS dans un squelette (transmettre une chaîne de langue)
1252 * ```
1253 * $x = '<:afficher_calendrier|texte_script:>';
1254 * ```
1255 *
1256 * @filtre
1257 * @param string $texte
1258 * Texte à échapper
1259 * @return string
1260 * Texte échappé
1261 **/
1262 function texte_script($texte) {
1263 return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1264 }
1265
1266
1267 /**
1268 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1269 *
1270 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1271 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1272 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1273 *
1274 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1275 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1276 * `$dossier_squelette` si définie qui resteront devant)
1277 *
1278 * Retourne dans tous les cas la liste des chemins.
1279 *
1280 * @note
1281 * Cette fonction est appelée à plusieurs endroits et crée une liste
1282 * de chemins finale à peu près de la sorte :
1283 *
1284 * - dossiers squelettes (si globale précisée)
1285 * - squelettes/
1286 * - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1287 * d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1288 * - racine du site
1289 * - squelettes-dist/
1290 * - prive/
1291 * - ecrire/
1292 *
1293 * @param string $dir_path
1294 * - Répertoire(s) à empiler au path
1295 * - '' provoque un recalcul des chemins.
1296 * @return array
1297 * Liste des chemins, par ordre de priorité.
1298 **/
1299 function _chemin($dir_path = null) {
1300 static $path_base = null;
1301 static $path_full = null;
1302 if ($path_base == null) {
1303 // Chemin standard depuis l'espace public
1304 $path = defined('_SPIP_PATH') ? _SPIP_PATH :
1305 _DIR_RACINE . ':' .
1306 _DIR_RACINE . 'squelettes-dist/:' .
1307 _DIR_RACINE . 'prive/:' .
1308 _DIR_RESTREINT;
1309 // Ajouter squelettes/
1310 if (@is_dir(_DIR_RACINE . 'squelettes')) {
1311 $path = _DIR_RACINE . 'squelettes/:' . $path;
1312 }
1313 foreach (explode(':', $path) as $dir) {
1314 if (strlen($dir) and substr($dir, -1) != '/') {
1315 $dir .= "/";
1316 }
1317 $path_base[] = $dir;
1318 }
1319 $path_full = $path_base;
1320 // Et le(s) dossier(s) des squelettes nommes
1321 if (strlen($GLOBALS['dossier_squelettes'])) {
1322 foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1323 array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1324 }
1325 }
1326 $GLOBALS['path_sig'] = md5(serialize($path_full));
1327 }
1328 if ($dir_path === null) {
1329 return $path_full;
1330 }
1331
1332 if (strlen($dir_path)) {
1333 $tete = "";
1334 if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1335 $tete = array_shift($path_base);
1336 }
1337 $dirs = array_reverse(explode(':', $dir_path));
1338 foreach ($dirs as $dir_path) {
1339 #if ($dir_path{0}!='/')
1340 # $dir_path = $dir_path;
1341 if (substr($dir_path, -1) != '/') {
1342 $dir_path .= "/";
1343 }
1344 if (!in_array($dir_path, $path_base)) {
1345 array_unshift($path_base, $dir_path);
1346 }
1347 }
1348 if (strlen($tete)) {
1349 array_unshift($path_base, $tete);
1350 }
1351 }
1352 $path_full = $path_base;
1353 // Et le(s) dossier(s) des squelettes nommes
1354 if (strlen($GLOBALS['dossier_squelettes'])) {
1355 foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1356 array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1357 }
1358 }
1359
1360 $GLOBALS['path_sig'] = md5(serialize($path_full));
1361
1362 return $path_full;
1363 }
1364
1365 /**
1366 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1367 *
1368 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1369 *
1370 * @uses _chemin()
1371 *
1372 * @return array Liste de chemins
1373 **/
1374 function creer_chemin() {
1375 $path_a = _chemin();
1376 static $c = '';
1377
1378 // on calcule le chemin si le dossier skel a change
1379 if ($c != $GLOBALS['dossier_squelettes']) {
1380 // assurer le non plantage lors de la montee de version :
1381 $c = $GLOBALS['dossier_squelettes'];
1382 $path_a = _chemin(''); // forcer un recalcul du chemin
1383 }
1384
1385 return $path_a;
1386 }
1387
1388
1389 function lister_themes_prives() {
1390 static $themes = null;
1391 if (is_null($themes)) {
1392 // si pas encore definie
1393 if (!defined('_SPIP_THEME_PRIVE')) {
1394 define('_SPIP_THEME_PRIVE', 'spip');
1395 }
1396 $themes = array(_SPIP_THEME_PRIVE);
1397 // lors d'une installation neuve, prefs n'est pas definie.
1398 if (isset($GLOBALS['visiteur_session']['prefs'])) {
1399 $prefs = $GLOBALS['visiteur_session']['prefs'];
1400 } else {
1401 $prefs = array();
1402 }
1403 if (is_string($prefs)) {
1404 $prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1405 }
1406 if (
1407 ((isset($prefs['theme']) and $theme = $prefs['theme'])
1408 or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1409 and $theme != _SPIP_THEME_PRIVE
1410 ) {
1411 array_unshift($themes, $theme);
1412 } // placer le theme choisi en tete
1413 }
1414
1415 return $themes;
1416 }
1417
1418 function find_in_theme($file, $subdir = '', $include = false) {
1419 static $themefiles = array();
1420 if (isset($themefiles["$subdir$file"])) {
1421 return $themefiles["$subdir$file"];
1422 }
1423 $themes = lister_themes_prives();
1424 foreach ($themes as $theme) {
1425 if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1426 return $themefiles["$subdir$file"] = $f;
1427 }
1428 }
1429 spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1430
1431 return $themefiles["$subdir$file"] = "";
1432 }
1433
1434
1435 /**
1436 * Cherche une image dans les dossiers d'images
1437 *
1438 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1439 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1440 * de facon temporaire le temps de la migration, et cherche de nouveau.
1441 *
1442 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1443 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1444 *
1445 * @see find_in_theme()
1446 * @see inc_icone_renommer_dist()
1447 *
1448 * @param string $icone
1449 * Nom de l'icone cherchée
1450 * @return string
1451 * Chemin complet de l'icone depuis la racine si l'icone est trouée,
1452 * sinon chaîne vide.
1453 **/
1454 function chemin_image($icone) {
1455 static $icone_renommer;
1456 // gerer le cas d'un double appel en evitant de refaire le travail inutilement
1457 if (strpos($icone, "/") !== false and file_exists($icone)) {
1458 return $icone;
1459 }
1460
1461 // si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1462 if (preg_match(',[.](png|gif|jpg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1463 return $f;
1464 }
1465 // sinon passer par le module de renommage
1466 if (is_null($icone_renommer)) {
1467 $icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1468 }
1469 if ($icone_renommer) {
1470 list($icone, $fonction) = $icone_renommer($icone, "");
1471 if (file_exists($icone)) {
1472 return $icone;
1473 }
1474 }
1475
1476 return find_in_path($icone, _NOM_IMG_PACK);
1477 }
1478
1479 //
1480 // chercher un fichier $file dans le SPIP_PATH
1481 // si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1482 // si 3e arg vrai, on inclut si ce n'est fait.
1483 $GLOBALS['path_sig'] = '';
1484 $GLOBALS['path_files'] = null;
1485
1486 /**
1487 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1488 *
1489 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1490 * suivant l'ordre des chemins connus de SPIP.
1491 *
1492 * @api
1493 * @see charger_fonction()
1494 * @uses creer_chemin() Pour la liste des chemins.
1495 * @example
1496 * ```
1497 * $f = find_in_path('css/perso.css');
1498 * $f = find_in_path('perso.css', 'css');
1499 * ```
1500 *
1501 * @param string $file
1502 * Fichier recherché
1503 * @param string $dirname
1504 * Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1505 * @param bool|string $include
1506 * - false : ne fait rien de plus
1507 * - true : inclut le fichier (include_once)
1508 * - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1509 * @return string|bool
1510 * - string : chemin du fichier trouvé
1511 * - false : fichier introuvable
1512 **/
1513 function find_in_path($file, $dirname = '', $include = false) {
1514 static $dirs = array();
1515 static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
1516 static $c = '';
1517
1518 // on calcule le chemin si le dossier skel a change
1519 if ($c != $GLOBALS['dossier_squelettes']) {
1520 // assurer le non plantage lors de la montee de version :
1521 $c = $GLOBALS['dossier_squelettes'];
1522 creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1523 }
1524
1525 if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1526 if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1527 return false;
1528 }
1529 if ($include and !isset($inc[$dirname][$file])) {
1530 include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1531 $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1532 }
1533
1534 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1535 }
1536
1537 $a = strrpos($file, '/');
1538 if ($a !== false) {
1539 $dirname .= substr($file, 0, ++$a);
1540 $file = substr($file, $a);
1541 }
1542
1543 foreach (creer_chemin() as $dir) {
1544 if (!isset($dirs[$a = $dir . $dirname])) {
1545 $dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1546 }
1547 if ($dirs[$a]) {
1548 if (file_exists(_ROOT_CWD . ($a .= $file))) {
1549 if ($include and !isset($inc[$dirname][$file])) {
1550 include_once _ROOT_CWD . $a;
1551 $inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1552 }
1553 if (!defined('_SAUVER_CHEMIN')) {
1554 // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1555 if (is_null($GLOBALS['path_files'])) {
1556 return $a;
1557 }
1558 define('_SAUVER_CHEMIN', true);
1559 }
1560
1561 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1562 }
1563 }
1564 }
1565
1566 if ($include) {
1567 spip_log("include_spip $dirname$file non trouve");
1568 if ($include === 'required') {
1569 echo '<pre>',
1570 "<strong>Erreur Fatale</strong><br />";
1571 if (function_exists('debug_print_backtrace')) {
1572 echo debug_print_backtrace();
1573 }
1574 echo '</pre>';
1575 die("Erreur interne: ne peut inclure $dirname$file");
1576 }
1577 }
1578
1579 if (!defined('_SAUVER_CHEMIN')) {
1580 // si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1581 if (is_null($GLOBALS['path_files'])) {
1582 return false;
1583 }
1584 define('_SAUVER_CHEMIN', true);
1585 }
1586
1587 return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1588 }
1589
1590 function clear_path_cache() {
1591 $GLOBALS['path_files'] = array();
1592 spip_unlink(_CACHE_CHEMIN);
1593 }
1594
1595 function load_path_cache() {
1596 // charger le path des plugins
1597 if (@is_readable(_CACHE_PLUGINS_PATH)) {
1598 include_once(_CACHE_PLUGINS_PATH);
1599 }
1600 $GLOBALS['path_files'] = array();
1601 // si le visiteur est admin,
1602 // on ne recharge pas le cache pour forcer sa mise a jour
1603 if (
1604 // la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1605 //AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1606 // utiliser le cookie est un pis aller qui marche 'en general'
1607 // on blinde par un second test au moment de la lecture de la session
1608 // !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1609 // et en ignorant ce cache en cas de recalcul explicite
1610 !_request('var_mode')
1611 ) {
1612 // on essaye de lire directement sans verrou pour aller plus vite
1613 if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1614 // mais si semble corrompu on relit avec un verrou
1615 if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1616 lire_fichier(_CACHE_CHEMIN, $contenu);
1617 if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1618 $GLOBALS['path_files'] = array();
1619 }
1620 }
1621 }
1622 }
1623 }
1624
1625 function save_path_cache() {
1626 if (defined('_SAUVER_CHEMIN')
1627 and _SAUVER_CHEMIN
1628 ) {
1629 ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1630 }
1631 }
1632
1633
1634 /**
1635 * Trouve tous les fichiers du path correspondants à un pattern
1636 *
1637 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1638 * par un `find_in_path()`
1639 *
1640 * @api
1641 * @uses creer_chemin()
1642 * @uses preg_files()
1643 *
1644 * @param string $dir
1645 * @param string $pattern
1646 * @param bool $recurs
1647 * @return array
1648 */
1649 function find_all_in_path($dir, $pattern, $recurs = false) {
1650 $liste_fichiers = array();
1651 $maxfiles = 10000;
1652
1653 // cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1654 // on a pas encore inclus flock.php
1655 if (!function_exists('preg_files')) {
1656 include_once _ROOT_RESTREINT . 'inc/flock.php';
1657 }
1658
1659 // Parcourir le chemin
1660 foreach (creer_chemin() as $d) {
1661 $f = $d . $dir;
1662 if (@is_dir($f)) {
1663 $liste = preg_files($f, $pattern, $maxfiles - count($liste_fichiers), $recurs === true ? array() : $recurs);
1664 foreach ($liste as $chemin) {
1665 $nom = basename($chemin);
1666 // ne prendre que les fichiers pas deja trouves
1667 // car find_in_path prend le premier qu'il trouve,
1668 // les autres sont donc masques
1669 if (!isset($liste_fichiers[$nom])) {
1670 $liste_fichiers[$nom] = $chemin;
1671 }
1672 }
1673 }
1674 }
1675
1676 return $liste_fichiers;
1677 }
1678
1679 /**
1680 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1681 * et beneficient d'une exception
1682 *
1683 * @param string $nom
1684 * @param bool $strict
1685 * @return bool
1686 */
1687 function autoriser_sans_cookie($nom, $strict = false) {
1688 static $autsanscookie = array('install', 'base_repair');
1689
1690 if (in_array($nom, $autsanscookie)) {
1691 if (test_espace_prive()){
1692 include_spip('base/connect_sql');
1693 if (!$strict or !spip_connect()){
1694 return true;
1695 }
1696 }
1697 }
1698 return false;
1699 }
1700
1701 /**
1702 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1703 *
1704 * @api
1705 * @param string $id
1706 * numero de la cle primaire si nombre, URL a decoder si pas numerique
1707 * @param string $entite
1708 * surnom de la table SQL (donne acces au nom de cle primaire)
1709 * @param string $args
1710 * query_string a placer apres cle=$id&....
1711 * @param string $ancre
1712 * ancre a mettre a la fin de l'URL a produire
1713 * @param bool|string $public
1714 * produire l'URL publique ou privee (par defaut: selon espace)
1715 * si string : serveur de base de donnee (nom du connect)
1716 * @param string $type
1717 * fichier dans le repertoire ecrire/urls determinant l'apparence
1718 * @return string|array
1719 * url codee ou fonction de decodage
1720 * array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1721 * (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1722 */
1723 function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1724 if ($public === null) {
1725 $public = !test_espace_prive();
1726 }
1727 $entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1728
1729 if (!$public) {
1730 if (!$entite) {
1731 return '';
1732 }
1733 if (!function_exists('generer_url_ecrire_objet')) {
1734 include_spip('inc/urls');
1735 }
1736 $res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1737 } else {
1738 if ($type === null) {
1739 $type = (isset($GLOBALS['type_urls']))
1740 ? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1741 : ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1742 ? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1743 }
1744
1745 $f = charger_fonction($type, 'urls', true);
1746 // se rabattre sur les urls page si les urls perso non dispo
1747 if (!$f) {
1748 $f = charger_fonction('page', 'urls', true);
1749 }
1750
1751 // si $entite='', on veut la fonction de passage URL ==> id
1752 // sinon on veut effectuer le passage id ==> URL
1753 if (!$entite) {
1754 return $f;
1755 }
1756
1757 // mais d'abord il faut tester le cas des urls sur une
1758 // base distante
1759 if (is_string($public)
1760 and $g = charger_fonction('connect', 'urls', true)
1761 ) {
1762 $f = $g;
1763 }
1764
1765 $res = $f(intval($id), $entite, $args, $ancre, $public);
1766
1767 }
1768 if ($res) {
1769 return $res;
1770 }
1771 // Sinon c'est un raccourci ou compat SPIP < 2
1772 if (!function_exists($f = 'generer_url_' . $entite)) {
1773 if (!function_exists($f .= '_dist')) {
1774 $f = '';
1775 }
1776 }
1777 if ($f) {
1778 $url = $f($id, $args, $ancre);
1779 if (strlen($args)) {
1780 $url .= strstr($url, '?')
1781 ? '&amp;' . $args
1782 : '?' . $args;
1783 }
1784
1785 return $url;
1786 }
1787 // On a ete gentil mais la ....
1788 spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1789
1790 return '';
1791 }
1792
1793 function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1794 $exec = objet_info($entite, 'url_edit');
1795 $url = generer_url_ecrire($exec, $args);
1796 if (intval($id)) {
1797 $url = parametre_url($url, id_table_objet($entite), $id);
1798 } else {
1799 $url = parametre_url($url, 'new', 'oui');
1800 }
1801 if ($ancre) {
1802 $url = ancre_url($url, $ancre);
1803 }
1804
1805 return $url;
1806 }
1807
1808 // http://code.spip.net/@urls_connect_dist
1809 function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1810 include_spip('base/connect_sql');
1811 $id_type = id_table_objet($entite, $public);
1812
1813 return _DIR_RACINE . get_spip_script('./')
1814 . "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1815 . (!$args ? '' : "&$args")
1816 . (!$ancre ? '' : "#$ancre");
1817 }
1818
1819
1820 /**
1821 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1822 *
1823 * @param string $url
1824 * @return string
1825 */
1826 function urlencode_1738($url) {
1827 if (preg_match(',[^\x00-\x7E],sS', $url)) {
1828 $uri = '';
1829 for ($i = 0; $i < strlen($url); $i++) {
1830 if (ord($a = $url[$i]) > 127) {
1831 $a = rawurlencode($a);
1832 }
1833 $uri .= $a;
1834 }
1835 $url = $uri;
1836 }
1837
1838 return quote_amp($url);
1839 }
1840
1841 // http://code.spip.net/@generer_url_entite_absolue
1842 function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1843 if (!$connect) {
1844 $connect = true;
1845 }
1846 $h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1847 if (!preg_match(',^\w+:,', $h)) {
1848 include_spip('inc/filtres_mini');
1849 $h = url_absolue($h);
1850 }
1851
1852 return $h;
1853 }
1854
1855
1856 /**
1857 * Tester qu'une variable d'environnement est active
1858 *
1859 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1860 * variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
1861 *
1862 * @param string|bool $truc
1863 * La valeur de la variable d'environnement
1864 * @return bool
1865 * true si la valeur est considérée active ; false sinon.
1866 **/
1867 function test_valeur_serveur($truc) {
1868 if (!$truc) {
1869 return false;
1870 }
1871
1872 return (strtolower($truc) !== 'off');
1873 }
1874
1875 //
1876 // Fonctions de fabrication des URL des scripts de Spip
1877 //
1878 /**
1879 * Calcule l'url de base du site
1880 *
1881 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1882 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1883 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1884 *
1885 * @note
1886 * La globale `$profondeur_url` doit être initialisée de manière à
1887 * indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1888 * racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1889 * la racine 0. Sur url/perso/ elle vaut 2
1890 *
1891 * @param int|boo|array $profondeur
1892 * - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1893 * - si int : indique que l'on veut l'url pour la profondeur indiquée
1894 * - si bool : retourne le tableau static complet
1895 * - si array : réinitialise le tableau static complet avec la valeur fournie
1896 * @return string|array
1897 */
1898 function url_de_base($profondeur = null) {
1899
1900 static $url = array();
1901 if (is_array($profondeur)) {
1902 return $url = $profondeur;
1903 }
1904 if ($profondeur === false) {
1905 return $url;
1906 }
1907
1908 if (is_null($profondeur)) {
1909 $profondeur = $GLOBALS['profondeur_url'];
1910 }
1911
1912 if (isset($url[$profondeur])) {
1913 return $url[$profondeur];
1914 }
1915
1916 $http = 'http';
1917
1918 if (
1919 isset($_SERVER["SCRIPT_URI"])
1920 and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1921 ) {
1922 $http = 'https';
1923 } elseif (
1924 isset($_SERVER['HTTPS'])
1925 and test_valeur_serveur($_SERVER['HTTPS'])
1926 ) {
1927 $http = 'https';
1928 }
1929
1930 // note : HTTP_HOST contient le :port si necessaire
1931 $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1932 // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1933 if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1934 $host = $GLOBALS['meta']['adresse_site'];
1935 if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1936 $http = $scheme;
1937 $host = str_replace("{$scheme}://", '', $host);
1938 }
1939 }
1940 if (isset($_SERVER['SERVER_PORT'])
1941 and $port = $_SERVER['SERVER_PORT']
1942 and strpos($host, ":") == false
1943 ) {
1944 if (!defined('_PORT_HTTP_STANDARD')) {
1945 define('_PORT_HTTP_STANDARD', '80');
1946 }
1947 if (!defined('_PORT_HTTPS_STANDARD')) {
1948 define('_PORT_HTTPS_STANDARD', '443');
1949 }
1950 if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1951 $host .= ":$port";
1952 }
1953 if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1954 $host .= ":$port";
1955 }
1956 }
1957
1958 if (!$GLOBALS['REQUEST_URI']) {
1959 if (isset($_SERVER['REQUEST_URI'])) {
1960 $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1961 } else {
1962 $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1963 if (!empty($_SERVER['QUERY_STRING'])
1964 and !strpos($_SERVER['REQUEST_URI'], '?')
1965 ) {
1966 $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1967 }
1968 }
1969 }
1970
1971 $url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1972
1973 return $url[$profondeur];
1974 }
1975
1976 /**
1977 * fonction testable de construction d'une url appelee par url_de_base()
1978 *
1979 * @param string $http
1980 * @param string $host
1981 * @param string $request
1982 * @param int $prof
1983 * @return string
1984 */
1985 function url_de_($http, $host, $request, $prof = 0) {
1986 $prof = max($prof, 0);
1987
1988 $myself = ltrim($request, '/');
1989 # supprimer la chaine de GET
1990 list($myself) = explode('?', $myself);
1991 // vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1992 // protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1993 if (strpos($myself,'://') !== false) {
1994 $myself = explode('://',$myself);
1995 array_shift($myself);
1996 $myself = implode('://',$myself);
1997 $myself = explode('/',$myself);
1998 array_shift($myself);
1999 $myself = implode('/',$myself);
2000 }
2001 $url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
2002
2003 $url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
2004
2005 return $url;
2006 }
2007
2008
2009 // Pour une redirection, la liste des arguments doit etre separee par "&"
2010 // Pour du code XHTML, ca doit etre &amp;
2011 // Bravo au W3C qui n'a pas ete capable de nous eviter ca
2012 // faute de separer proprement langage et meta-langage
2013
2014 // Attention, X?y=z et "X/?y=z" sont completement differents!
2015 // http://httpd.apache.org/docs/2.0/mod/mod_dir.html
2016
2017 /**
2018 * Crée une URL vers un script de l'espace privé
2019 *
2020 * @example
2021 * ```
2022 * generer_url_ecrire('admin_plugin')
2023 * ```
2024 *
2025 * @param string $script
2026 * Nom de la page privée (xx dans exec=xx)
2027 * @param string $args
2028 * Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2029 * @param bool $no_entities
2030 * Si false : transforme les `&` en `&amp;`
2031 * @param bool|string $rel
2032 * URL relative ?
2033 *
2034 * - false : l’URL sera complète et contiendra l’URL du site
2035 * - true : l’URL sera relavive.
2036 * - string : on transmet l'url à la fonction
2037 * @return string URL
2038 **/
2039 function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2040 if (!$rel) {
2041 $rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
2042 } else {
2043 if (!is_string($rel)) {
2044 $rel = _DIR_RESTREINT ? _DIR_RESTREINT :
2045 ('./' . _SPIP_ECRIRE_SCRIPT);
2046 }
2047 }
2048
2049 list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2050 if ($script and ($script <> 'accueil' or $rel)) {
2051 $args = "?exec=$script" . (!$args ? '' : "&$args");
2052 } elseif ($args) {
2053 $args = "?$args";
2054 }
2055 if ($ancre) {
2056 $args .= "#$ancre";
2057 }
2058
2059 return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2060 }
2061
2062 /**
2063 * Permet d'ajouter lien vers une page privée à un paramètre d'url (déprécié)
2064 *
2065 * ```
2066 * // deprecié
2067 * $h = generer_url_ecrire('article', "id_article=$id_article&redirect=" . generer_url_retour('articles'));
2068 * // utiliser
2069 * $h = generer_url_ecrire('article');
2070 * $h = parametre_url($h, 'id_article', $id_article);
2071 * $h = parametre_url($h, 'redirect', generer_url_ecrire('articles'));
2072 * ```
2073 *
2074 * @deprecated Utiliser parametre_url() et generer_url_ecrire()
2075 * @see parametre_url()
2076 * @see generer_url_ecrire()
2077 *
2078 * @param string $script
2079 * @param string $args
2080 * @return string
2081 */
2082 function generer_url_retour($script, $args = "") {
2083 return rawurlencode(generer_url_ecrire($script, $args, true, true));
2084 }
2085
2086 //
2087 // Adresse des scripts publics (a passer dans inc-urls...)
2088 //
2089
2090
2091 /**
2092 * Retourne le nom du fichier d'exécution de SPIP
2093 *
2094 * @see _SPIP_SCRIPT
2095 * @note
2096 * Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2097 * dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2098 *
2099 * @param string $default
2100 * Script par défaut
2101 * @return string
2102 * Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2103 **/
2104 function get_spip_script($default = '') {
2105 # cas define('_SPIP_SCRIPT', '');
2106 if (_SPIP_SCRIPT) {
2107 return _SPIP_SCRIPT;
2108 } else {
2109 return $default;
2110 }
2111 }
2112
2113 /**
2114 * Crée une URL vers une page publique de SPIP
2115 *
2116 * @example
2117 * ```
2118 * generer_url_public("rubrique","id_rubrique=$id_rubrique")
2119 * ```
2120 *
2121 * @param string $script
2122 * Nom de la page
2123 * @param string|array $args
2124 * Arguments à transmettre a l'URL,
2125 * soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2126 * soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2127 * @param bool $no_entities
2128 * Si false : transforme les `&` en `&amp;`
2129 * @param bool $rel
2130 * URL relative ?
2131 *
2132 * - false : l’URL sera complète et contiendra l’URL du site
2133 * - true : l’URL sera relavive.
2134 * @param string $action
2135 * - Fichier d'exécution public (spip.php par défaut)
2136 * @return string URL
2137 **/
2138 function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2139 // si le script est une action (spip_pass, spip_inscription),
2140 // standardiser vers la nouvelle API
2141
2142 if (!$action) {
2143 $action = get_spip_script();
2144 }
2145 if ($script) {
2146 $action = parametre_url($action, _SPIP_PAGE, $script, '&');
2147 }
2148
2149 if ($args) {
2150 if (is_array($args)) {
2151 $r = '';
2152 foreach ($args as $k => $v) {
2153 $r .= '&' . $k . '=' . $v;
2154 }
2155 $args = substr($r, 1);
2156 }
2157 $action .=
2158 (strpos($action, '?') !== false ? '&' : '?') . $args;
2159 }
2160 if (!$no_entities) {
2161 $action = quote_amp($action);
2162 }
2163
2164 // ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2165 return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2166 }
2167
2168 // http://code.spip.net/@generer_url_prive
2169 function generer_url_prive($script, $args = "", $no_entities = false) {
2170
2171 return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2172 }
2173
2174 // Pour les formulaires en methode POST,
2175 // mettre le nom du script a la fois en input-hidden et dans le champ action:
2176 // 1) on peut ainsi memoriser le signet comme si c'etait un GET
2177 // 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2178
2179 /**
2180 * Retourne un formulaire (POST par défaut) vers un script exec
2181 * de l’interface privée
2182 *
2183 * @param string $script
2184 * Nom de la page exec
2185 * @param string $corps
2186 * Contenu du formulaire
2187 * @param string $atts
2188 * Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2189 * @param string $submit
2190 * Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2191 * @return string
2192 * Code HTML du formulaire
2193 **/
2194 function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2195
2196 $script1 = explode('&', $script);
2197 $script1 = reset($script1);
2198
2199 return "<form action='"
2200 . ($script ? generer_url_ecrire($script) : '')
2201 . "' "
2202 . ($atts ? $atts : " method='post'")
2203 . "><div>\n"
2204 . "<input type='hidden' name='exec' value='$script1' />"
2205 . $corps
2206 . (!$submit ? '' :
2207 ("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2208 . "</div></form>\n";
2209 }
2210
2211 /**
2212 * Générer un formulaire pour lancer une action vers $script
2213 *
2214 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2215 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2216 * qui ne sont pas destines a etre mis en signets
2217 *
2218 * @param string $script
2219 * @param string $corps
2220 * @param string $atts
2221 * @param bool $public
2222 * @return string
2223 */
2224 function generer_form_action($script, $corps, $atts = '', $public = false) {
2225 // si l'on est dans l'espace prive, on garde dans l'url
2226 // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2227 // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2228 $h = (_DIR_RACINE and !$public)
2229 ? generer_url_ecrire(_request('exec'))
2230 : generer_url_public();
2231
2232 return "\n<form action='" .
2233 $h .
2234 "'" .
2235 $atts .
2236 ">\n" .
2237 "<div>" .
2238 "\n<input type='hidden' name='action' value='$script' />" .
2239 $corps .
2240 "</div></form>";
2241 }
2242
2243 /**
2244 * Créer une URL
2245 *
2246 * @param string $script
2247 * Nom du script à exécuter
2248 * @param string $args
2249 * Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2250 * @param bool $no_entities
2251 * Si false : transforme les & en &amp;
2252 * @param boolean $public
2253 * URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2254 * true : l’URL sera relative.
2255 * @return string
2256 * URL
2257 */
2258 function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2259 // si l'on est dans l'espace prive, on garde dans l'url
2260 // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2261 // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2262 $url = (_DIR_RACINE and !$public)
2263 ? generer_url_ecrire(_request('exec'))
2264 : generer_url_public('', '', false, false);
2265 $url = parametre_url($url, 'action', $script);
2266 if ($args) {
2267 $url .= quote_amp('&' . $args);
2268 }
2269
2270 if ($no_entities) {
2271 $url = str_replace('&amp;', '&', $url);
2272 }
2273
2274 return $url;
2275 }
2276
2277
2278 /**
2279 * Fonction d'initialisation groupée pour compatibilité ascendante
2280 *
2281 * @param string $pi Répertoire permanent inaccessible
2282 * @param string $pa Répertoire permanent accessible
2283 * @param string $ti Répertoire temporaire inaccessible
2284 * @param string $ta Répertoire temporaire accessible
2285 */
2286 function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2287 spip_initialisation_core($pi, $pa, $ti, $ta);
2288 spip_initialisation_suite();
2289 }
2290
2291 /**
2292 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2293 *
2294 * Elle définit les répertoires et fichiers non partageables
2295 * et indique dans $test_dirs ceux devant être accessibles en écriture
2296 * mais ne touche pas à cette variable si elle est déjà définie
2297 * afin que mes_options.php puisse en spécifier d'autres.
2298 *
2299 * Elle définit ensuite les noms des fichiers et les droits.
2300 * Puis simule un register_global=on sécurisé.
2301 *
2302 * @param string $pi Répertoire permanent inaccessible
2303 * @param string $pa Répertoire permanent accessible
2304 * @param string $ti Répertoire temporaire inaccessible
2305 * @param string $ta Répertoire temporaire accessible
2306 */
2307 function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2308 static $too_late = 0;
2309 if ($too_late++) {
2310 return;
2311 }
2312
2313 // Declaration des repertoires
2314
2315 // le nom du repertoire plugins/ activables/desactivables
2316 if (!defined('_DIR_PLUGINS')) {
2317 define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2318 }
2319
2320 // le nom du repertoire des extensions/ permanentes du core, toujours actives
2321 if (!defined('_DIR_PLUGINS_DIST')) {
2322 define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2323 }
2324
2325 // le nom du repertoire des librairies
2326 if (!defined('_DIR_LIB')) {
2327 define('_DIR_LIB', _DIR_RACINE . "lib/");
2328 }
2329
2330 if (!defined('_DIR_IMG')) {
2331 define('_DIR_IMG', $pa);
2332 }
2333 if (!defined('_DIR_LOGOS')) {
2334 define('_DIR_LOGOS', $pa);
2335 }
2336 if (!defined('_DIR_IMG_ICONES')) {
2337 define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2338 }
2339
2340 if (!defined('_DIR_DUMP')) {
2341 define('_DIR_DUMP', $ti . "dump/");
2342 }
2343 if (!defined('_DIR_SESSIONS')) {
2344 define('_DIR_SESSIONS', $ti . "sessions/");
2345 }
2346 if (!defined('_DIR_TRANSFERT')) {
2347 define('_DIR_TRANSFERT', $ti . "upload/");
2348 }
2349 if (!defined('_DIR_CACHE')) {
2350 define('_DIR_CACHE', $ti . "cache/");
2351 }
2352 if (!defined('_DIR_CACHE_XML')) {
2353 define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2354 }
2355 if (!defined('_DIR_SKELS')) {
2356 define('_DIR_SKELS', _DIR_CACHE . "skel/");
2357 }
2358 if (!defined('_DIR_AIDE')) {
2359 define('_DIR_AIDE', _DIR_CACHE . "aide/");
2360 }
2361 if (!defined('_DIR_TMP')) {
2362 define('_DIR_TMP', $ti);
2363 }
2364
2365 if (!defined('_DIR_VAR')) {
2366 define('_DIR_VAR', $ta);
2367 }
2368
2369 if (!defined('_DIR_ETC')) {
2370 define('_DIR_ETC', $pi);
2371 }
2372 if (!defined('_DIR_CONNECT')) {
2373 define('_DIR_CONNECT', $pi);
2374 }
2375 if (!defined('_DIR_CHMOD')) {
2376 define('_DIR_CHMOD', $pi);
2377 }
2378
2379 if (!isset($GLOBALS['test_dirs']))
2380 // Pas $pi car il est bon de le mettre hors ecriture apres intstall
2381 // il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2382 {
2383 $GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2384 }
2385
2386 // Declaration des fichiers
2387
2388 if (!defined('_CACHE_PLUGINS_PATH')) {
2389 define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2390 }
2391 if (!defined('_CACHE_PLUGINS_OPT')) {
2392 define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2393 }
2394 if (!defined('_CACHE_PLUGINS_FCT')) {
2395 define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2396 }
2397 if (!defined('_CACHE_PIPELINES')) {
2398 define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2399 }
2400 if (!defined('_CACHE_CHEMIN')) {
2401 define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2402 }
2403
2404 # attention .php obligatoire pour ecrire_fichier_securise
2405 if (!defined('_FILE_META')) {
2406 define('_FILE_META', $ti . 'meta_cache.php');
2407 }
2408 if (!defined('_DIR_LOG')) {
2409 define('_DIR_LOG', _DIR_TMP . 'log/');
2410 }
2411 if (!defined('_FILE_LOG')) {
2412 define('_FILE_LOG', 'spip');
2413 }
2414 if (!defined('_FILE_LOG_SUFFIX')) {
2415 define('_FILE_LOG_SUFFIX', '.log');
2416 }
2417
2418 // Le fichier de connexion a la base de donnees
2419 // tient compte des anciennes versions (inc_connect...)
2420 if (!defined('_FILE_CONNECT_INS')) {
2421 define('_FILE_CONNECT_INS', 'connect');
2422 }
2423 if (!defined('_FILE_CONNECT')) {
2424 define('_FILE_CONNECT',
2425 (@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2426 : (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2427 : false)));
2428 }
2429
2430 // Le fichier de reglages des droits
2431 if (!defined('_FILE_CHMOD_INS')) {
2432 define('_FILE_CHMOD_INS', 'chmod');
2433 }
2434 if (!defined('_FILE_CHMOD')) {
2435 define('_FILE_CHMOD',
2436 (@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2437 : false));
2438 }
2439
2440 if (!defined('_FILE_LDAP')) {
2441 define('_FILE_LDAP', 'ldap.php');
2442 }
2443
2444 if (!defined('_FILE_TMP_SUFFIX')) {
2445 define('_FILE_TMP_SUFFIX', '.tmp.php');
2446 }
2447 if (!defined('_FILE_CONNECT_TMP')) {
2448 define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2449 }
2450 if (!defined('_FILE_CHMOD_TMP')) {
2451 define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2452 }
2453
2454 // Definition des droits d'acces en ecriture
2455 if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2456 include_once _FILE_CHMOD;
2457 }
2458
2459 // Se mefier des fichiers mal remplis!
2460 if (!defined('_SPIP_CHMOD')) {
2461 define('_SPIP_CHMOD', 0777);
2462 }
2463
2464 if (!defined('_DEFAULT_CHARSET')) {
2465 /** Le charset par défaut lors de l'installation */
2466 define('_DEFAULT_CHARSET', 'utf-8');
2467 }
2468 if (!defined('_ROOT_PLUGINS')) {
2469 define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2470 }
2471 if (!defined('_ROOT_PLUGINS_DIST')) {
2472 define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2473 }
2474 if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2475 define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2476 }
2477
2478 // La taille des Log
2479 if (!defined('_MAX_LOG')) {
2480 define('_MAX_LOG', 100);
2481 }
2482
2483 // Sommes-nous dans l'empire du Mal ?
2484 // (ou sous le signe du Pingouin, ascendant GNU ?)
2485 if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2486 if (!defined('_OS_SERVEUR')) {
2487 define('_OS_SERVEUR', 'windows');
2488 }
2489 if (!defined('_SPIP_LOCK_MODE')) {
2490 define('_SPIP_LOCK_MODE', 1);
2491 } // utiliser le flock php
2492 } else {
2493 if (!defined('_OS_SERVEUR')) {
2494 define('_OS_SERVEUR', '');
2495 }
2496 if (!defined('_SPIP_LOCK_MODE')) {
2497 define('_SPIP_LOCK_MODE', 1);
2498 } // utiliser le flock php
2499 #if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2500 }
2501
2502 // Langue par defaut
2503 if (!defined('_LANGUE_PAR_DEFAUT')) {
2504 define('_LANGUE_PAR_DEFAUT', 'fr');
2505 }
2506
2507 //
2508 // Module de lecture/ecriture/suppression de fichiers utilisant flock()
2509 // (non surchargeable en l'etat ; attention si on utilise include_spip()
2510 // pour le rendre surchargeable, on va provoquer un reecriture
2511 // systematique du noyau ou une baisse de perfs => a etudier)
2512 include_once _ROOT_RESTREINT . 'inc/flock.php';
2513
2514 // charger tout de suite le path et son cache
2515 load_path_cache();
2516
2517 // *********** traiter les variables ************
2518
2519 //
2520 // Securite
2521 //
2522
2523 // Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2524 if (isset($_REQUEST['GLOBALS'])) {
2525 die();
2526 }
2527 // nettoyer les magic quotes \' et les caracteres nuls %00
2528 spip_desinfecte($_GET);
2529 spip_desinfecte($_POST);
2530 spip_desinfecte($_COOKIE);
2531 spip_desinfecte($_REQUEST);
2532
2533 // Si les variables sont passees en global par le serveur,
2534 // il faut faire quelques verifications de base
2535 // Todo: test à supprimer lorsque version PHP minimum >= 5.4.
2536 $avertir_register_globals = false;
2537 if (test_valeur_serveur(@ini_get('register_globals'))) {
2538 // ne pas desinfecter les globales en profondeur car elle contient aussi les
2539 // precedentes, qui seraient desinfectees 2 fois.
2540 spip_desinfecte($GLOBALS, false);
2541 // plugin grenier
2542 if (include_spip('inc/php3')) {
2543 spip_register_globals(true);
2544 }
2545
2546 $avertir_register_globals = true;
2547 }
2548
2549 // appliquer le cookie_prefix
2550 if ($GLOBALS['cookie_prefix'] != 'spip') {
2551 include_spip('inc/cookie');
2552 recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2553 }
2554
2555 //
2556 // Capacites php (en fonction de la version)
2557 //
2558 $GLOBALS['flag_ob'] = (function_exists("ob_start")
2559 && function_exists("ini_get")
2560 && !strstr(@ini_get('disable_functions'), 'ob_'));
2561 $GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2562 $GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2563 $GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2564 (get_cfg_var('upload_max_filesize') > 0));
2565
2566
2567 // Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2568 if (isset($_SERVER['REQUEST_URI'])) {
2569 $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2570 } else {
2571 $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2572 if (!empty($_SERVER['QUERY_STRING'])
2573 and !strpos($_SERVER['REQUEST_URI'], '?')
2574 ) {
2575 $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2576 }
2577 }
2578
2579 // Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2580 if (!defined('_RENOUVELLE_ALEA')) {
2581 define('_RENOUVELLE_ALEA', 12 * 3600);
2582 }
2583 if (!defined('_DUREE_COOKIE_ADMIN')) {
2584 define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2585 }
2586
2587 // charger les meta si possible et renouveller l'alea au besoin
2588 // charge aussi effacer_meta et ecrire_meta
2589 $inc_meta = charger_fonction('meta', 'inc');
2590 $inc_meta();
2591
2592 // on a pas pu le faire plus tot
2593 if ($avertir_register_globals) {
2594 avertir_auteurs("register_globals",
2595 _L("Probl&egrave;me de s&eacute;curit&eacute; : register_globals=on; dans php.ini &agrave; corriger."));
2596 }
2597
2598 // nombre de repertoires depuis la racine
2599 // on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2600 // ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2601 // le calcul est faux)
2602 if (!_DIR_RESTREINT) {
2603 $GLOBALS['profondeur_url'] = 1;
2604 } else {
2605 $uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2606 $uri_ref = $_SERVER["SCRIPT_NAME"];
2607 if (!$uri_ref
2608 // si on est appele avec un autre ti, on est sans doute en mutu
2609 // si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2610 // a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2611 // s'en remettre a l'adresse du site. alea jacta est.
2612 or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2613 ) {
2614
2615 if (isset($GLOBALS['meta']['adresse_site'])) {
2616 $uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2617 $uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2618 } else {
2619 $uri_ref = "";
2620 }
2621 }
2622 if (!$uri or !$uri_ref) {
2623 $GLOBALS['profondeur_url'] = 0;
2624 } else {
2625 $GLOBALS['profondeur_url'] = max(0,
2626 substr_count($uri[0], '/')
2627 - substr_count($uri_ref, '/'));
2628 }
2629 }
2630 // s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2631 if (_FILE_CONNECT) {
2632 if (verifier_visiteur() == '0minirezo'
2633 // si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2634 and !isset($_COOKIE['spip_admin'])
2635 ) {
2636 clear_path_cache();
2637 }
2638 }
2639
2640 }
2641
2642 /**
2643 * Complements d'initialisation non critiques pouvant etre realises
2644 * par les plugins
2645 *
2646 */
2647 function spip_initialisation_suite() {
2648 static $too_late = 0;
2649 if ($too_late++) {
2650 return;
2651 }
2652
2653 // taille mini des login
2654 if (!defined('_LOGIN_TROP_COURT')) {
2655 define('_LOGIN_TROP_COURT', 4);
2656 }
2657
2658 // la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2659 #if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2660 #if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2661 #if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2662
2663 // la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2664 #if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2665 #if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2666 #if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2667 #if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2668
2669 if (!defined('_PASS_LONGUEUR_MINI')) {
2670 define('_PASS_LONGUEUR_MINI', 6);
2671 }
2672
2673
2674 // Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2675 if (!defined('_IMG_QUALITE')) {
2676 define('_IMG_QUALITE', 85);
2677 } # valeur par defaut
2678 if (!defined('_IMG_GD_QUALITE')) {
2679 define('_IMG_GD_QUALITE', _IMG_QUALITE);
2680 } # surcharge pour la lib GD
2681 if (!defined('_IMG_CONVERT_QUALITE')) {
2682 define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2683 } # surcharge pour imagick en ligne de commande
2684 // Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2685 if (!defined('_IMG_IMAGICK_QUALITE')) {
2686 define('_IMG_IMAGICK_QUALITE', 75);
2687 } # surcharge pour imagick en PHP
2688
2689 if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2690 define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2691 } // poids en octet
2692
2693 // qq chaines standard
2694 if (!defined('_ACCESS_FILE_NAME')) {
2695 define('_ACCESS_FILE_NAME', '.htaccess');
2696 }
2697 if (!defined('_AUTH_USER_FILE')) {
2698 define('_AUTH_USER_FILE', '.htpasswd');
2699 }
2700 if (!defined('_SPIP_DUMP')) {
2701 define('_SPIP_DUMP', 'dump@nom_site@@stamp@.xml');
2702 }
2703 if (!defined('_CACHE_RUBRIQUES')) {
2704 /** Fichier cache pour le navigateur de rubrique du bandeau */
2705 define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2706 }
2707 if (!defined('_CACHE_RUBRIQUES_MAX')) {
2708 /** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2709 define('_CACHE_RUBRIQUES_MAX', 500);
2710 }
2711
2712 if (!defined('_EXTENSION_SQUELETTES')) {
2713 define('_EXTENSION_SQUELETTES', 'html');
2714 }
2715
2716 if (!defined('_DOCTYPE_ECRIRE')) {
2717 define('_DOCTYPE_ECRIRE',
2718 // "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
2719 //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
2720 //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
2721 // "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1 //EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>\n");
2722 "<!DOCTYPE html>\n");
2723 }
2724 if (!defined('_DOCTYPE_AIDE')) {
2725 define('_DOCTYPE_AIDE',
2726 "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2727 }
2728
2729 /** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2730 * le script de l'espace public, alias index.php */
2731 if (!defined('_SPIP_SCRIPT')) {
2732 define('_SPIP_SCRIPT', 'spip.php');
2733 }
2734 /** Argument page, personalisable en cas de conflit avec un autre script */
2735 if (!defined('_SPIP_PAGE')) {
2736 define('_SPIP_PAGE', 'page');
2737 }
2738
2739 // le script de l'espace prive
2740 // Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2741 // les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2742 // meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2743 if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2744 define('_SPIP_ECRIRE_SCRIPT', (empty($_SERVER['SERVER_SOFTWARE']) ? '' :
2745 preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE']) ?
2746 'index.php' : ''));
2747 }
2748
2749
2750 if (!defined('_SPIP_AJAX')) {
2751 define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2752 ? 1
2753 : (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2754 }
2755
2756 // La requete est-elle en ajax ?
2757 if (!defined('_AJAX')) {
2758 define('_AJAX',
2759 (isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2760 or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2761 or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2762 or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2763 )
2764 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
2765 );
2766 }
2767
2768 # nombre de pixels maxi pour calcul de la vignette avec gd
2769 # au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2770 # les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2771 if (!defined('_IMG_GD_MAX_PIXELS')) {
2772 define('_IMG_GD_MAX_PIXELS',
2773 (isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2774 ? $GLOBALS['meta']['max_taille_vignettes']
2775 : 0);
2776 }
2777
2778 if (!defined('_MEMORY_LIMIT_MIN')) {
2779 define('_MEMORY_LIMIT_MIN', 16);
2780 } // en Mo
2781 // si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2782 // on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2783 // il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2784 if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2785 if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2786 $unit = strtolower(substr($memory, -1));
2787 $memory = substr($memory, 0, -1);
2788 switch ($unit) {
2789 // Le modifieur 'G' est disponible depuis PHP 5.1.0
2790 case 'g':
2791 $memory *= 1024;
2792 case 'm':
2793 $memory *= 1024;
2794 case 'k':
2795 $memory *= 1024;
2796 }
2797 if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2798 @ini_set('memory_limit', $m = _MEMORY_LIMIT_MIN . 'M');
2799 if (trim(ini_get('memory_limit')) != $m) {
2800 if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2801 define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2802 } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2803 }
2804 }
2805 } else {
2806 if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2807 define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2808 }
2809 } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2810 }
2811 // Protocoles a normaliser dans les chaines de langues
2812 if (!defined('_PROTOCOLES_STD')) {
2813 define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2814 }
2815
2816 init_var_mode();
2817 }
2818
2819 /**
2820 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2821 * la validité du cache ou certains affichages spéciaux.
2822 *
2823 * Le paramètre d'URL `var_mode` permet de
2824 * modifier la pérennité du cache, recalculer des urls
2825 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2826 * d'afficher un écran de débug ou des traductions non réalisées.
2827 *
2828 * En fonction de ces paramètres dans l'URL appelante, on définit
2829 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2830 *
2831 * Le paramètre `var_mode` accepte ces valeurs :
2832 *
2833 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2834 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2835 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2836 * - `debug` : modifie l'affichage activant le mode "debug"
2837 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2838 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2839 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2840 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2841 *
2842 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2843 *
2844 * @note
2845 * Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2846 * le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2847 * ` var_mode` (calcul ou recalcul).
2848 */
2849 function init_var_mode() {
2850 static $done = false;
2851 if (!$done) {
2852
2853 if (isset($_GET['var_mode'])) {
2854 $var_mode = explode(',', $_GET['var_mode']);
2855 // tout le monde peut calcul/recalcul
2856 if (!defined('_VAR_MODE')) {
2857 if (in_array('recalcul', $var_mode)) {
2858 define('_VAR_MODE', 'recalcul');
2859 } elseif (in_array('calcul', $var_mode)) {
2860 define('_VAR_MODE', 'calcul');
2861 }
2862 }
2863 $var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2864 if ($var_mode) {
2865 include_spip('inc/autoriser');
2866 // autoriser preview si preview seulement, et sinon autoriser debug
2867 if (autoriser(
2868 ($_GET['var_mode'] == 'preview')
2869 ? 'previsualiser'
2870 : 'debug'
2871 )) {
2872 if (in_array('traduction', $var_mode)) {
2873 // forcer le calcul pour passer dans traduire
2874 if (!defined('_VAR_MODE')) {
2875 define('_VAR_MODE', 'calcul');
2876 }
2877 // et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2878 if (!defined('_VAR_NOCACHE')) {
2879 define('_VAR_NOCACHE', true);
2880 }
2881 $var_mode = array_diff($var_mode, array('traduction'));
2882 }
2883 if (in_array('preview', $var_mode)) {
2884 // basculer sur les criteres de preview dans les boucles
2885 if (!defined('_VAR_PREVIEW')) {
2886 define('_VAR_PREVIEW', true);
2887 }
2888 // forcer le calcul
2889 if (!defined('_VAR_MODE')) {
2890 define('_VAR_MODE', 'calcul');
2891 }
2892 // et ne pas enregistrer de cache
2893 if (!defined('_VAR_NOCACHE')) {
2894 define('_VAR_NOCACHE', true);
2895 }
2896 $var_mode = array_diff($var_mode, array('preview'));
2897 }
2898 if (in_array('inclure', $var_mode)) {
2899 // forcer le compilo et ignorer les caches existants
2900 if (!defined('_VAR_MODE')) {
2901 define('_VAR_MODE', 'calcul');
2902 }
2903 if (!defined('_VAR_INCLURE')) {
2904 define('_VAR_INCLURE', true);
2905 }
2906 // et ne pas enregistrer de cache
2907 if (!defined('_VAR_NOCACHE')) {
2908 define('_VAR_NOCACHE', true);
2909 }
2910 $var_mode = array_diff($var_mode, array('inclure'));
2911 }
2912 if (in_array('urls', $var_mode)) {
2913 // forcer le compilo et ignorer les caches existants
2914 if (!defined('_VAR_MODE')) {
2915 define('_VAR_MODE', 'calcul');
2916 }
2917 if (!defined('_VAR_URLS')) {
2918 define('_VAR_URLS', true);
2919 }
2920 $var_mode = array_diff($var_mode, array('urls'));
2921 }
2922 if (in_array('images', $var_mode)) {
2923 // forcer le compilo et ignorer les caches existants
2924 if (!defined('_VAR_MODE')) {
2925 define('_VAR_MODE', 'calcul');
2926 }
2927 // indiquer qu'on doit recalculer les images
2928 if (!defined('_VAR_IMAGES')) {
2929 define('_VAR_IMAGES', true);
2930 }
2931 $var_mode = array_diff($var_mode, array('images'));
2932 }
2933 if (in_array('debug', $var_mode)) {
2934 if (!defined('_VAR_MODE')) {
2935 define('_VAR_MODE', 'debug');
2936 }
2937 // et ne pas enregistrer de cache
2938 if (!defined('_VAR_NOCACHE')) {
2939 define('_VAR_NOCACHE', true);
2940 }
2941 $var_mode = array_diff($var_mode, array('debug'));
2942 }
2943 if (count($var_mode) and !defined('_VAR_MODE')) {
2944 define('_VAR_MODE', reset($var_mode));
2945 }
2946 if (isset($GLOBALS['visiteur_session']['nom'])) {
2947 spip_log($GLOBALS['visiteur_session']['nom']
2948 . " " . _VAR_MODE);
2949 }
2950 } // pas autorise ?
2951 else {
2952 // si on n'est pas connecte on se redirige
2953 if (!$GLOBALS['visiteur_session']) {
2954 include_spip('inc/headers');
2955 redirige_par_entete(generer_url_public('login',
2956 'url=' . rawurlencode(
2957 parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2958 ), true));
2959 }
2960 // sinon tant pis
2961 }
2962 }
2963 }
2964 if (!defined('_VAR_MODE')) {
2965 /**
2966 * Indique le mode de calcul ou d'affichage de la page.
2967 * @see init_var_mode()
2968 */
2969 define('_VAR_MODE', false);
2970 }
2971 $done = true;
2972 }
2973 }
2974
2975 // Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2976 // supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2977 // la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2978 // http://code.spip.net/@spip_desinfecte
2979 function spip_desinfecte(&$t, $deep = true) {
2980 static $magic_quotes;
2981 if (!isset($magic_quotes)) {
2982 $magic_quotes = @get_magic_quotes_gpc();
2983 }
2984
2985 foreach ($t as $key => $val) {
2986 if (is_string($t[$key])) {
2987 if ($magic_quotes) {
2988 $t[$key] = stripslashes($t[$key]);
2989 }
2990 $t[$key] = str_replace(chr(0), '-', $t[$key]);
2991 } // traiter aussi les "texte_plus" de article_edit
2992 else {
2993 if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2994 spip_desinfecte($t[$key], $deep);
2995 }
2996 }
2997 }
2998 }
2999
3000 // retourne le statut du visiteur s'il s'annonce
3001
3002 // http://code.spip.net/@verifier_visiteur
3003 function verifier_visiteur() {
3004 // Rq: pour que cette fonction marche depuis mes_options
3005 // il faut forcer l'init si ce n'est fait
3006 // mais on risque de perturber des plugins en initialisant trop tot
3007 // certaines constantes
3008 @spip_initialisation_core(
3009 (_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
3010 (_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
3011 (_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
3012 (_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
3013 );
3014
3015 // Demarrer une session NON AUTHENTIFIEE si on donne son nom
3016 // dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
3017 // Attention on separe bien session_nom et nom, pour eviter
3018 // les melanges entre donnees SQL et variables plus aleatoires
3019 $variables_session = array('session_nom', 'session_email');
3020 foreach ($variables_session as $var) {
3021 if (_request($var) !== null) {
3022 $init = true;
3023 break;
3024 }
3025 }
3026 if (isset($init)) {
3027 #@spip_initialisation_suite();
3028 $session = charger_fonction('session', 'inc');
3029 $session();
3030 include_spip('inc/texte');
3031 foreach ($variables_session as $var) {
3032 if (($a = _request($var)) !== null) {
3033 $GLOBALS['visiteur_session'][$var] = safehtml($a);
3034 }
3035 }
3036 if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
3037 $GLOBALS['visiteur_session']['id_auteur'] = 0;
3038 }
3039 $session($GLOBALS['visiteur_session']);
3040
3041 return 0;
3042 }
3043
3044 $h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
3045 if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
3046
3047 $session = charger_fonction('session', 'inc');
3048 if ($session()) {
3049 return $GLOBALS['visiteur_session']['statut'];
3050 }
3051 if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
3052 include_spip('inc/auth');
3053 $h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
3054 }
3055 if ($h) {
3056 $GLOBALS['visiteur_session'] = $h;
3057
3058 return $GLOBALS['visiteur_session']['statut'];
3059 }
3060 }
3061
3062 // au moins son navigateur nous dit la langue preferee de cet inconnu
3063 include_spip('inc/lang');
3064 utiliser_langue_visiteur();
3065
3066 return false;
3067 }
3068
3069
3070 /**
3071 * Sélectionne la langue donnée en argument et mémorise la courante
3072 *
3073 * Restaure l'ancienne langue si appellée sans argument.
3074 *
3075 * @note
3076 * On pourrait économiser l'empilement en cas de non changemnt
3077 * et lui faire retourner `False` pour prevenir l'appelant
3078 * Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3079 * cette fonction retourne toujours non `False`
3080 *
3081 * @uses changer_langue()
3082 * @param null|string $lang
3083 * - string : Langue à appliquer,
3084 * - null : Pour restituer la dernière langue mémorisée.
3085 * @return string
3086 * - string Langue utilisée.
3087 **/
3088 function lang_select($lang = null) {
3089 static $pile_langues = array();
3090 if (!function_exists('changer_langue')) {
3091 include_spip('inc/lang');
3092 }
3093 if ($lang === null) {
3094 $lang = array_pop($pile_langues);
3095 } else {
3096 array_push($pile_langues, $GLOBALS['spip_lang']);
3097 }
3098 if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3099 return $lang;
3100 }
3101 changer_langue($lang);
3102
3103 return $lang;
3104 }
3105
3106 /**
3107 * Renvoie une chaîne qui identifie la session courante
3108 *
3109 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3110 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3111 * de fichier cache.
3112 *
3113 * @pipeline_appel definir_session
3114 *
3115 * @param bool $force
3116 * @return string
3117 * Identifiant de la session
3118 **/
3119 function spip_session($force = false) {
3120 static $session;
3121 if ($force or !isset($session)) {
3122 $s = pipeline('definir_session',
3123 $GLOBALS['visiteur_session']
3124 ? serialize($GLOBALS['visiteur_session'])
3125 . '_' . @$_COOKIE['spip_session']
3126 : ''
3127 );
3128 $session = $s ? substr(md5($s), 0, 8) : '';
3129 }
3130
3131 #spip_log('session: '.$session);
3132 return $session;
3133 }
3134
3135
3136 /**
3137 * Retourne un lien vers une aide
3138 *
3139 * Aide, aussi depuis l'espace privé à présent.
3140 * Surchargeable mais pas d'erreur fatale si indisponible.
3141 *
3142 * @param string $aide
3143 * Cle d'identification de l'aide desiree
3144 * @param bool $distante
3145 * Generer une url locale (par defaut)
3146 * ou une url distante [directement sur spip.net]
3147 * @return
3148 * Lien sur une icone d'aide
3149 **/
3150 function aider($aide = '', $distante = false) {
3151 $aider = charger_fonction('aide', 'inc', true);
3152
3153 return $aider ? $aider($aide, '', array(), $distante) : '';
3154 }
3155
3156 /**
3157 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3158 *
3159 * Si l’utiliseur est un webmestre.
3160 */
3161 function exec_info_dist() {
3162
3163 include_spip('inc/autoriser');
3164 if (autoriser('webmestre')) {
3165 $cookies_masques = ['spip_session', 'PHPSESSID'];
3166 $cookies_backup = [];
3167 foreach ($cookies_masques as $k) {
3168 if (!empty($_COOKIE[$k])) {
3169 $cookies_backup[$k] = $_COOKIE[$k];
3170 $_COOKIE[$k] = '******************************';
3171 }
3172 }
3173 phpinfo();
3174 foreach ($cookies_backup as $k => $v) {
3175 $_COOKIE[$k] = $v;
3176 }
3177 } else {
3178 include_spip('inc/filtres');
3179 sinon_interdire_acces();
3180 }
3181 }
3182
3183 /**
3184 * Génère une erreur de squelette
3185 *
3186 * Génère une erreur de squelette qui sera bien visible par un
3187 * administrateur authentifié lors d'une visite de la page en erreur
3188 *
3189 * @param bool|string|array $message
3190 * - Message d'erreur (string|array)
3191 * - false pour retourner le texte des messages d'erreurs
3192 * - vide pour afficher les messages d'erreurs
3193 * @param string|array|object $lieu
3194 * Lieu d'origine de l'erreur
3195 * @return null|string
3196 * - Rien dans la plupart des cas
3197 * - string si $message à false.
3198 **/
3199 function erreur_squelette($message = '', $lieu = '') {
3200 $debusquer = charger_fonction('debusquer', 'public');
3201 if (is_array($lieu)) {
3202 include_spip('public/compiler');
3203 $lieu = reconstruire_contexte_compil($lieu);
3204 }
3205
3206 return $debusquer($message, $lieu);
3207 }
3208
3209 /**
3210 * Calcule un squelette avec un contexte et retourne son contenu
3211 *
3212 * La fonction de base de SPIP : un squelette + un contexte => une page.
3213 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3214 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3215 * $options permet de selectionner les options suivantes :
3216 *
3217 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3218 * - raw => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3219 * pour chaque $fond fourni.
3220 *
3221 * @api
3222 * @param string /array $fond
3223 * - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3224 * - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3225 * @param array $contexte
3226 * - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3227 * - La langue est transmise automatiquement (sauf option étoile).
3228 * @param array $options
3229 * Options complémentaires :
3230 *
3231 * - trim : applique un trim sur le résultat (true par défaut)
3232 * - raw : retourne un tableau d'information sur le squelette (false par défaut)
3233 * - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3234 * équivalent de INCLURE*
3235 * - ajax : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3236 * @param string $connect
3237 * Non du connecteur de bdd a utiliser
3238 * @return string|array
3239 * - Contenu du squelette calculé
3240 * - ou tableau d'information sur le squelette.
3241 */
3242 function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3243 if (!function_exists('evaluer_fond')) {
3244 include_spip('public/assembler');
3245 }
3246 // assurer la compat avec l'ancienne syntaxe
3247 // (trim etait le 3eme argument, par defaut a true)
3248 if (!is_array($options)) {
3249 $options = array('trim' => $options);
3250 }
3251 if (!isset($options['trim'])) {
3252 $options['trim'] = true;
3253 }
3254
3255 if (isset($contexte['connect'])) {
3256 $connect = $contexte['connect'];
3257 unset($contexte['connect']);
3258 }
3259
3260 $texte = "";
3261 $pages = array();
3262 $lang_select = '';
3263 if (!isset($options['etoile']) or !$options['etoile']) {
3264 // Si on a inclus sans fixer le critere de lang, on prend la langue courante
3265 if (!isset($contexte['lang'])) {
3266 $contexte['lang'] = $GLOBALS['spip_lang'];
3267 }
3268
3269 if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3270 $lang_select = lang_select($contexte['lang']);
3271 }
3272 }
3273
3274 if (!isset($GLOBALS['_INC_PUBLIC'])) {
3275 $GLOBALS['_INC_PUBLIC'] = 0;
3276 }
3277
3278 $GLOBALS['_INC_PUBLIC']++;
3279
3280
3281 foreach (is_array($fond) ? $fond : array($fond) as $f) {
3282 $page = evaluer_fond($f, $contexte, $connect);
3283 if ($page === '') {
3284 $c = isset($options['compil']) ? $options['compil'] : '';
3285 $a = array('fichier' => $fond);
3286 $erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3287 erreur_squelette($erreur, $c);
3288 // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3289 $page = array('texte' => '', 'erreur' => $erreur);
3290 }
3291
3292 $page = pipeline('recuperer_fond', array(
3293 'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3294 'data' => $page
3295 ));
3296 if (isset($options['ajax']) and $options['ajax']) {
3297 if (!function_exists('encoder_contexte_ajax')) {
3298 include_spip('inc/filtres');
3299 }
3300 $page['texte'] = encoder_contexte_ajax(
3301 array_merge(
3302 $contexte,
3303 array('fond' => $f),
3304 ($connect ? array('connect' => $connect) : array())
3305 ),
3306 '',
3307 $page['texte'],
3308 $options['ajax']
3309 );
3310 }
3311
3312 if (isset($options['raw']) and $options['raw']) {
3313 $pages[] = $page;
3314 } else {
3315 $texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3316 }
3317 }
3318
3319 $GLOBALS['_INC_PUBLIC']--;
3320
3321 if ($lang_select) {
3322 lang_select();
3323 }
3324 if (isset($options['raw']) and $options['raw']) {
3325 return is_array($fond) ? $pages : reset($pages);
3326 } else {
3327 return $options['trim'] ? ltrim($texte) : $texte;
3328 }
3329 }
3330
3331 /**
3332 * Trouve un squelette dans le repertoire modeles/
3333 *
3334 * @param $nom
3335 * @return string
3336 */
3337 function trouve_modele($nom) {
3338 return trouver_fond($nom, 'modeles/');
3339 }
3340
3341 /**
3342 * Trouver un squelette dans le chemin
3343 * on peut specifier un sous-dossier dans $dir
3344 * si $pathinfo est a true, retourne un tableau avec
3345 * les composantes du fichier trouve
3346 * + le chemin complet sans son extension dans fond
3347 *
3348 * @param string $nom
3349 * @param string $dir
3350 * @param bool $pathinfo
3351 * @return array|string
3352 */
3353 function trouver_fond($nom, $dir = '', $pathinfo = false) {
3354 $f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3355 if (!$pathinfo) {
3356 return $f;
3357 }
3358 // renvoyer un tableau detaille si $pathinfo==true
3359 $p = pathinfo($f);
3360 if (!isset($p['extension']) or !$p['extension']) {
3361 $p['extension'] = _EXTENSION_SQUELETTES;
3362 }
3363 if (!isset($p['extension']) or !$p['filename']) {
3364 $p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3365 }
3366 $p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3367
3368 return $p;
3369 }
3370
3371 /**
3372 * Teste, pour un nom de page de l'espace privé, s'il est possible
3373 * de générer son contenu.
3374 *
3375 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3376 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3377 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3378 *
3379 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3380 * ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3381 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3382 *
3383 * @param string $nom
3384 * Nom de la page
3385 * @return string
3386 * Nom de l'exec, sinon chaîne vide.
3387 **/
3388 function tester_url_ecrire($nom) {
3389 static $exec = array();
3390 if (isset($exec[$nom])) {
3391 return $exec[$nom];
3392 }
3393 // tester si c'est une page en squelette
3394 if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3395 return $exec[$nom] = 'fond';
3396 } // compat skels orthogonaux version precedente
3397 elseif (trouver_fond($nom, 'prive/exec/')) {
3398 return $exec[$nom] = 'fond_monobloc';
3399 } // echafaudage d'un fond !
3400 elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3401 return $exec[$nom] = 'fond';
3402 }
3403 // attention, il ne faut pas inclure l'exec ici
3404 // car sinon #URL_ECRIRE provoque des inclusions
3405 // et des define intrusifs potentiels
3406 return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3407 }
3408
3409
3410 /**
3411 * Tente de charger dynamiquement une extension PHP
3412 *
3413 * @example
3414 * ```
3415 * $ok = charger_php_extension('sqlite');
3416 * ```
3417 * @uses inc_charger_php_extension_dist() Si la librairie n'est pas déjà charchée
3418 *
3419 * @param string $module Nom du module à charger
3420 * @return bool true si le module est chargé
3421 **/
3422 function charger_php_extension($module) {
3423 if (extension_loaded($module)) {
3424 return true;
3425 } else {
3426 $charger_php_extension = charger_fonction('charger_php_extension', 'inc');
3427
3428 return $charger_php_extension($module);
3429 }
3430 }
3431
3432
3433 /**
3434 * Indique si le code HTML5 est permis sur le site public
3435 *
3436 * @return bool
3437 * true si et seulement si la configuration autorise le code HTML5 sur le site public
3438 **/
3439 function html5_permis() {
3440 return (isset($GLOBALS['meta']['version_html_max'])
3441 and ('html5' == $GLOBALS['meta']['version_html_max']));
3442 }
3443
3444 /*
3445 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3446 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3447 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3448 */
3449
3450 /**
3451 * lire_meta : fonction dépréciée
3452 *
3453 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3454 * @see lire_config()
3455 * @param string $nom Clé de meta à lire
3456 * @return mixed Valeur de la meta.
3457 **/
3458 function lire_meta($nom) {
3459 return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3460 }
3461
3462
3463 /**
3464 * ecrire_metas : fonction dépréciée
3465 *
3466 * @deprecated
3467 **/
3468 function ecrire_metas() { }
3469
3470 /**
3471 * Retourne une ligne d'un résultat de requête mysql (déprécié)
3472 *
3473 * @see sql_fetch()
3474 * @deprecated Utiliser sql_fetch()
3475 * @param Ressource $r Ressource mysql
3476 * @param int|null $t Type de retour
3477 * @return array|void|bool Tableau de la ligne SQL
3478 **/
3479 function spip_fetch_array($r, $t = null) {
3480 if (!isset($t)) {
3481 if ($r) {
3482 return sql_fetch($r);
3483 }
3484 } else {
3485 if ($t == 'SPIP_NUM') {
3486 $t = MYSQLI_NUM;
3487 }
3488 if ($t == 'SPIP_BOTH') {
3489 $t = MYSQLI_BOTH;
3490 }
3491 if ($t == 'SPIP_ASSOC') {
3492 $t = MYSQLI_ASSOC;
3493 }
3494 spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
3495 if ($r) {
3496 return mysqli_fetch_array($r, $t);
3497 }
3498 }
3499 }
3500
3501 /**
3502 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3503 * au prochain passage dans l'espace prive
3504 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3505 * les alertes affichees une fois sont effacees
3506 *
3507 * @param string $nom
3508 * @param string $message
3509 * @param string $statut
3510 */
3511 function avertir_auteurs($nom, $message, $statut = '') {
3512 $alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3513 if (!$alertes
3514 or !is_array($alertes = unserialize($alertes))
3515 ) {
3516 $alertes = array();
3517 }
3518
3519 if (!isset($alertes[$statut])) {
3520 $alertes[$statut] = array();
3521 }
3522 $alertes[$statut][$nom] = $message;
3523 ecrire_meta("message_alertes_auteurs", serialize($alertes));
3524 }
3525
3526 if (PHP_VERSION_ID < 50500) {
3527 if (!function_exists('array_column')) {
3528 /**
3529 * Returns the values from a single column of the input array, identified by
3530 * the $columnKey.
3531 *
3532 * Optionally, you may provide an $indexKey to index the values in the returned
3533 * array by the values from the $indexKey column in the input array.
3534 *
3535 * @link http://php.net/manual/fr/function.array-column.php
3536 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3537 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3538 * @license http://opensource.org/licenses/MIT MIT
3539 *
3540 * @param array $input A multi-dimensional array (record set) from which to pull
3541 * a column of values.
3542 * @param mixed $columnKey The column of values to return. This value may be the
3543 * integer key of the column you wish to retrieve, or it
3544 * may be the string key name for an associative array.
3545 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3546 * the returned array. This value may be the integer key
3547 * of the column, or it may be the string key name.
3548 * @return array
3549 */
3550 function array_column($input = null, $columnKey = null, $indexKey = null)
3551 {
3552 // Using func_get_args() in order to check for proper number of
3553 // parameters and trigger errors exactly as the built-in array_column()
3554 // does in PHP 5.5.
3555 $argc = func_num_args();
3556 $params = func_get_args();
3557
3558 if ($argc < 2) {
3559 trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3560 return null;
3561 }
3562
3563 if (!is_array($params[0])) {
3564 trigger_error(
3565 'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3566 E_USER_WARNING
3567 );
3568 return null;
3569 }
3570
3571 if (!is_int($params[1])
3572 && !is_float($params[1])
3573 && !is_string($params[1])
3574 && $params[1] !== null
3575 && !(is_object($params[1]) && method_exists($params[1], '__toString'))
3576 ) {
3577 trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3578 return false;
3579 }
3580
3581 if (isset($params[2])
3582 && !is_int($params[2])
3583 && !is_float($params[2])
3584 && !is_string($params[2])
3585 && !(is_object($params[2]) && method_exists($params[2], '__toString'))
3586 ) {
3587 trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3588 return false;
3589 }
3590
3591 $paramsInput = $params[0];
3592 $paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3593
3594 $paramsIndexKey = null;
3595 if (isset($params[2])) {
3596 if (is_float($params[2]) || is_int($params[2])) {
3597 $paramsIndexKey = (int) $params[2];
3598 } else {
3599 $paramsIndexKey = (string) $params[2];
3600 }
3601 }
3602
3603 $resultArray = array();
3604
3605 foreach ($paramsInput as $row) {
3606 $key = $value = null;
3607 $keySet = $valueSet = false;
3608
3609 if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3610 $keySet = true;
3611 $key = (string) $row[$paramsIndexKey];
3612 }
3613
3614 if ($paramsColumnKey === null) {
3615 $valueSet = true;
3616 $value = $row;
3617 } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3618 $valueSet = true;
3619 $value = $row[$paramsColumnKey];
3620 }
3621
3622 if ($valueSet) {
3623 if ($keySet) {
3624 $resultArray[$key] = $value;
3625 } else {
3626 $resultArray[] = $value;
3627 }
3628 }
3629
3630 }
3631
3632 return $resultArray;
3633 }
3634
3635 }
3636 }
3637
3638 /**
3639 * Nettoie une chaine pour servir comme classes CSS.
3640 *
3641 * @note
3642 * les classes CSS acceptent théoriquement tous les caractères sauf NUL.
3643 * Ici, on limite (enlève) les caractères autres qu’alphanumérique, espace, - + _ @
3644 *
3645 * @param string|string[] $classes
3646 * @return string|string[]
3647 */
3648 function spip_sanitize_classname($classes) {
3649 if (is_array($classes)) {
3650 return array_map('spip_sanitize_classname', $classes);
3651 }
3652 return preg_replace("/[^ 0-9a-z_\-+@]/i", "", $classes);
3653 }