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