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