source: spip-zone/_plugins_/cachefix/trunk/inc/utils.fix.3.2.php @ 113542

Last change on this file since 113542 was 113542, checked in by jluc@…, 4 months ago

Fix le bug de sessionnement des caches SPIP https://core.spip.net/issues/4235
À tester en vérifiant et pour vérifier que les caches devant être sessionnés le sont bien
Regarder la taille du cache avec fix / sans fix donne la mesure des caches multipliés à tort et inutilement calculés

Ce fixe détecte la version SPIP pour surcharger les fichiers du core public/assembler et public/balises avec la version de la bonne branche.
! le fix pour inc/utils est aussi livré, mais ce fichier ne peut être surchargé par un plugin. Il faut donc copier la version de ecrire/inc dans ecrire/inc/utils.php.sav et écraser avec la version livrée dans le plugin !
Testé pour SPIP3.3 dev
Retours bienvenus

File size: 103.8 KB
Line 
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
19if (!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 */
50function 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 */
116function 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 **/
150function 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 **/
170function 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 **/
196function 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 */
242function 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 */
324function 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 **/
363function 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 **/
382function _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 **/
430function 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 */
459function 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 */
492function 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 */
597function 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 */
621function 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 */
639function 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 **/
659function 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 */
710function 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 */
720function 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 */
751function _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 */
823function _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)) {
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 */
884function 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 */
915function 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
949function 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 **/
975function 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 */
995function 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 */
1046function 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 */
1067function 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 */
1082function 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 */
1104function 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 */
1145function 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 **/
1170function 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 **/
1225function 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 **/
1262function _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 **/
1337function 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
1352function 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
1381function 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 **/
1417function 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 **/
1476function 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
1553function clear_path_cache() {
1554        $GLOBALS['path_files'] = array();
1555        spip_unlink(_CACHE_CHEMIN);
1556}
1557
1558function 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
1588function 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 */
1612function 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 * @param string $nom
1645 * @return bool
1646 */
1647function autoriser_sans_cookie($nom) {
1648        static $autsanscookie = array('install', 'base_repair');
1649        $nom = preg_replace('/.php[3]?$/', '', basename($nom));
1650
1651        return in_array($nom, $autsanscookie);
1652}
1653
1654/**
1655 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1656 *
1657 * @api
1658 * @param string $id
1659 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1660 * @param string $entite
1661 *   surnom de la table SQL (donne acces au nom de cle primaire)
1662 * @param string $args
1663 *   query_string a placer apres cle=$id&....
1664 * @param string $ancre
1665 *   ancre a mettre a la fin de l'URL a produire
1666 * @param bool|string $public
1667 *   produire l'URL publique ou privee (par defaut: selon espace)
1668 *   si string : serveur de base de donnee (nom du connect)
1669 * @param string $type
1670 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1671 * @return string|array
1672 *   url codee ou fonction de decodage
1673 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1674 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1675 */
1676function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1677        if ($public === null) {
1678                $public = !test_espace_prive();
1679        }
1680        $entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1681
1682        if (!$public) {
1683                if (!$entite) {
1684                        return '';
1685                }
1686                if (!function_exists('generer_url_ecrire_objet')) {
1687                        include_spip('inc/urls');
1688                }
1689                $res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1690        } else {
1691                if ($type === null) {
1692                        $type = (isset($GLOBALS['type_urls']))
1693                                ? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1694                                : ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1695                                        ? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1696                }
1697
1698                $f = charger_fonction($type, 'urls', true);
1699                // se rabattre sur les urls page si les urls perso non dispo
1700                if (!$f) {
1701                        $f = charger_fonction('page', 'urls', true);
1702                }
1703
1704                // si $entite='', on veut la fonction de passage URL ==> id
1705                // sinon on veut effectuer le passage id ==> URL
1706                if (!$entite) {
1707                        return $f;
1708                }
1709
1710                // mais d'abord il faut tester le cas des urls sur une
1711                // base distante
1712                if (is_string($public)
1713                        and $g = charger_fonction('connect', 'urls', true)
1714                ) {
1715                        $f = $g;
1716                }
1717
1718                $res = $f(intval($id), $entite, $args, $ancre, $public);
1719
1720        }
1721        if ($res) {
1722                return $res;
1723        }
1724        // Sinon c'est un raccourci ou compat SPIP < 2
1725        if (!function_exists($f = 'generer_url_' . $entite)) {
1726                if (!function_exists($f .= '_dist')) {
1727                        $f = '';
1728                }
1729        }
1730        if ($f) {
1731                $url = $f($id, $args, $ancre);
1732                if (strlen($args)) {
1733                        $url .= strstr($url, '?')
1734                                ? '&amp;' . $args
1735                                : '?' . $args;
1736                }
1737
1738                return $url;
1739        }
1740        // On a ete gentil mais la ....
1741        spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1742
1743        return '';
1744}
1745
1746function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1747        $exec = objet_info($entite, 'url_edit');
1748        $url = generer_url_ecrire($exec, $args);
1749        if (intval($id)) {
1750                $url = parametre_url($url, id_table_objet($entite), $id);
1751        } else {
1752                $url = parametre_url($url, 'new', 'oui');
1753        }
1754        if ($ancre) {
1755                $url = ancre_url($url, $ancre);
1756        }
1757
1758        return $url;
1759}
1760
1761// http://code.spip.net/@urls_connect_dist
1762function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1763        include_spip('base/connect_sql');
1764        $id_type = id_table_objet($entite, $public);
1765
1766        return _DIR_RACINE . get_spip_script('./')
1767        . "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1768        . (!$args ? '' : "&$args")
1769        . (!$ancre ? '' : "#$ancre");
1770}
1771
1772
1773/**
1774 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1775 *
1776 * @param string $url
1777 * @return string
1778 */
1779function urlencode_1738($url) {
1780        if (preg_match(',[^\x00-\x7E],sS', $url)) {
1781                $uri = '';
1782                for ($i = 0; $i < strlen($url); $i++) {
1783                        if (ord($a = $url[$i]) > 127) {
1784                                $a = rawurlencode($a);
1785                        }
1786                        $uri .= $a;
1787                }
1788                $url = $uri;
1789        }
1790
1791        return quote_amp($url);
1792}
1793
1794// http://code.spip.net/@generer_url_entite_absolue
1795function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1796        if (!$connect) {
1797                $connect = true;
1798        }
1799        $h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1800        if (!preg_match(',^\w+:,', $h)) {
1801                include_spip('inc/filtres_mini');
1802                $h = url_absolue($h);
1803        }
1804
1805        return $h;
1806}
1807
1808
1809/**
1810 * Tester qu'une variable d'environnement est active
1811 *
1812 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1813 * variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
1814 *
1815 * @param string|bool $truc
1816 *     La valeur de la variable d'environnement
1817 * @return bool
1818 *     true si la valeur est considérée active ; false sinon.
1819 **/
1820function test_valeur_serveur($truc) {
1821        if (!$truc) {
1822                return false;
1823        }
1824
1825        return (strtolower($truc) !== 'off');
1826}
1827
1828//
1829// Fonctions de fabrication des URL des scripts de Spip
1830//
1831/**
1832 * Calcule l'url de base du site
1833 *
1834 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1835 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1836 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1837 *
1838 * @note
1839 *     La globale `$profondeur_url` doit être initialisée de manière à
1840 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1841 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1842 *     la racine 0. Sur url/perso/ elle vaut 2
1843 *
1844 * @param int|boo|array $profondeur
1845 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1846 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1847 *    - si bool : retourne le tableau static complet
1848 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1849 * @return string|array
1850 */
1851function url_de_base($profondeur = null) {
1852
1853        static $url = array();
1854        if (is_array($profondeur)) {
1855                return $url = $profondeur;
1856        }
1857        if ($profondeur === false) {
1858                return $url;
1859        }
1860
1861        if (is_null($profondeur)) {
1862                $profondeur = $GLOBALS['profondeur_url'];
1863        }
1864
1865        if (isset($url[$profondeur])) {
1866                return $url[$profondeur];
1867        }
1868
1869        $http = 'http';
1870
1871        if (
1872                isset($_SERVER["SCRIPT_URI"])
1873                and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1874        ) {
1875                $http = 'https';
1876        } elseif (
1877                isset($_SERVER['HTTPS'])
1878                and test_valeur_serveur($_SERVER['HTTPS'])
1879        ) {
1880                $http = 'https';
1881        }
1882
1883        // note : HTTP_HOST contient le :port si necessaire
1884        $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1885        // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1886        if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1887                $host = $GLOBALS['meta']['adresse_site'];
1888                if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1889                        $http = $scheme;
1890                        $host = str_replace("{$scheme}://", '', $host);
1891                }
1892        }
1893        if (isset($_SERVER['SERVER_PORT'])
1894                and $port = $_SERVER['SERVER_PORT']
1895                and strpos($host, ":") == false
1896        ) {
1897                if (!defined('_PORT_HTTP_STANDARD')) {
1898                        define('_PORT_HTTP_STANDARD', '80');
1899                }
1900                if (!defined('_PORT_HTTPS_STANDARD')) {
1901                        define('_PORT_HTTPS_STANDARD', '443');
1902                }
1903                if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1904                        $host .= ":$port";
1905                }
1906                if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1907                        $host .= ":$port";
1908                }
1909        }
1910
1911        if (!$GLOBALS['REQUEST_URI']) {
1912                if (isset($_SERVER['REQUEST_URI'])) {
1913                        $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1914                } else {
1915                        $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1916                        if (!empty($_SERVER['QUERY_STRING'])
1917                                and !strpos($_SERVER['REQUEST_URI'], '?')
1918                        ) {
1919                                $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1920                        }
1921                }
1922        }
1923
1924        $url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1925
1926        return $url[$profondeur];
1927}
1928
1929/**
1930 * fonction testable de construction d'une url appelee par url_de_base()
1931 *
1932 * @param string $http
1933 * @param string $host
1934 * @param string $request
1935 * @param int $prof
1936 * @return string
1937 */
1938function url_de_($http, $host, $request, $prof = 0) {
1939        $prof = max($prof, 0);
1940
1941        $myself = ltrim($request, '/');
1942        # supprimer la chaine de GET
1943        list($myself) = explode('?', $myself);
1944        // vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1945        // protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1946        if (strpos($myself,'://') !== false) {
1947                $myself = explode('://',$myself);
1948                array_shift($myself);
1949                $myself = implode('://',$myself);
1950                $myself = explode('/',$myself);
1951                array_shift($myself);
1952                $myself = implode('/',$myself);
1953        }
1954        $url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1955
1956        $url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1957
1958        return $url;
1959}
1960
1961
1962// Pour une redirection, la liste des arguments doit etre separee par "&"
1963// Pour du code XHTML, ca doit etre &amp;
1964// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1965// faute de separer proprement langage et meta-langage
1966
1967// Attention, X?y=z et "X/?y=z" sont completement differents!
1968// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1969
1970/**
1971 * Crée une URL vers un script de l'espace privé
1972 *
1973 * @example
1974 *     ```
1975 *     generer_url_ecrire('admin_plugin')
1976 *     ```
1977 *
1978 * @param string $script
1979 *     Nom de la page privée (xx dans exec=xx)
1980 * @param string $args
1981 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
1982 * @param bool $no_entities
1983 *     Si false : transforme les `&` en `&amp;`
1984 * @param bool|string $rel
1985 *     URL relative ?
1986 *
1987 *     - false : l’URL sera complète et contiendra l’URL du site
1988 *     - true : l’URL sera relavive.
1989 *     - string : on transmet l'url à la fonction
1990 * @return string URL
1991 **/
1992function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
1993        if (!$rel) {
1994                $rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
1995        } else {
1996                if (!is_string($rel)) {
1997                        $rel = _DIR_RESTREINT ? _DIR_RESTREINT :
1998                                ('./' . _SPIP_ECRIRE_SCRIPT);
1999                }
2000        }
2001
2002        list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2003        if ($script and ($script <> 'accueil' or $rel)) {
2004                $args = "?exec=$script" . (!$args ? '' : "&$args");
2005        } elseif ($args) {
2006                $args = "?$args";
2007        }
2008        if ($ancre) {
2009                $args .= "#$ancre";
2010        }
2011
2012        return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2013}
2014
2015/**
2016 * Permet d'ajouter lien vers une page privée à un paramètre d'url (déprécié)
2017 *
2018 *     ```
2019 *     // deprecié
2020 *     $h = generer_url_ecrire('article', "id_article=$id_article&redirect=" . generer_url_retour('articles'));
2021 *     // utiliser
2022 *     $h = generer_url_ecrire('article');
2023 *     $h = parametre_url($h, 'id_article', $id_article);
2024 *     $h = parametre_url($h, 'redirect', generer_url_ecrire('articles'));
2025 *     ```
2026 *
2027 * @deprecated Utiliser parametre_url() et generer_url_ecrire()
2028 * @see parametre_url()
2029 * @see generer_url_ecrire()
2030 *
2031 * @param string $script
2032 * @param string $args
2033 * @return string
2034 */
2035function generer_url_retour($script, $args = "") {
2036        return rawurlencode(generer_url_ecrire($script, $args, true, true));
2037}
2038
2039//
2040// Adresse des scripts publics (a passer dans inc-urls...)
2041//
2042
2043
2044/**
2045 * Retourne le nom du fichier d'exécution de SPIP
2046 *
2047 * @see _SPIP_SCRIPT
2048 * @note
2049 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2050 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2051 *
2052 * @param string $default
2053 *     Script par défaut
2054 * @return string
2055 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2056 **/
2057function get_spip_script($default = '') {
2058        # cas define('_SPIP_SCRIPT', '');
2059        if (_SPIP_SCRIPT) {
2060                return _SPIP_SCRIPT;
2061        } else {
2062                return $default;
2063        }
2064}
2065
2066/**
2067 * Crée une URL vers une page publique de SPIP
2068 *
2069 * @example
2070 *     ```
2071 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2072 *     ```
2073 *
2074 * @param string $script
2075 *     Nom de la page
2076 * @param string|array $args
2077 *     Arguments à transmettre a l'URL,
2078 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2079 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2080 * @param bool $no_entities
2081 *     Si false : transforme les `&` en `&amp;`
2082 * @param bool $rel
2083 *     URL relative ?
2084 *
2085 *     - false : l’URL sera complète et contiendra l’URL du site
2086 *     - true : l’URL sera relavive.
2087 * @param string $action
2088 *     - Fichier d'exécution public (spip.php par défaut)
2089 * @return string URL
2090 **/
2091function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2092        // si le script est une action (spip_pass, spip_inscription),
2093        // standardiser vers la nouvelle API
2094
2095        if (!$action) {
2096                $action = get_spip_script();
2097        }
2098        if ($script) {
2099                $action = parametre_url($action, _SPIP_PAGE, $script, '&');
2100        }
2101
2102        if ($args) {
2103                if (is_array($args)) {
2104                        $r = '';
2105                        foreach ($args as $k => $v) {
2106                                $r .= '&' . $k . '=' . $v;
2107                        }
2108                        $args = substr($r, 1);
2109                }
2110                $action .=
2111                        (strpos($action, '?') !== false ? '&' : '?') . $args;
2112        }
2113        if (!$no_entities) {
2114                $action = quote_amp($action);
2115        }
2116
2117        // ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2118        return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2119}
2120
2121// http://code.spip.net/@generer_url_prive
2122function generer_url_prive($script, $args = "", $no_entities = false) {
2123
2124        return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2125}
2126
2127// Pour les formulaires en methode POST,
2128// mettre le nom du script a la fois en input-hidden et dans le champ action:
2129// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2130// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2131
2132/**
2133 * Retourne un formulaire (POST par défaut) vers un script exec
2134 * de l’interface privée
2135 *
2136 * @param string $script
2137 *     Nom de la page exec
2138 * @param string $corps
2139 *     Contenu du formulaire
2140 * @param string $atts
2141 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2142 * @param string $submit
2143 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2144 * @return string
2145 *     Code HTML du formulaire
2146 **/
2147function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2148
2149        $script1 = explode('&', $script);
2150        $script1 = reset($script1);
2151
2152        return "<form action='"
2153        . ($script ? generer_url_ecrire($script) : '')
2154        . "' "
2155        . ($atts ? $atts : " method='post'")
2156        . "><div>\n"
2157        . "<input type='hidden' name='exec' value='$script1' />"
2158        . $corps
2159        . (!$submit ? '' :
2160                ("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2161        . "</div></form>\n";
2162}
2163
2164/**
2165 * Générer un formulaire pour lancer une action vers $script
2166 *
2167 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2168 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2169 * qui ne sont pas destines a etre mis en signets
2170 *
2171 * @param string $script
2172 * @param string $corps
2173 * @param string $atts
2174 * @param bool $public
2175 * @return string
2176 */
2177function generer_form_action($script, $corps, $atts = '', $public = false) {
2178        // si l'on est dans l'espace prive, on garde dans l'url
2179        // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2180        // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2181        $h = (_DIR_RACINE and !$public)
2182                ? generer_url_ecrire(_request('exec'))
2183                : generer_url_public();
2184
2185        return "\n<form action='" .
2186        $h .
2187        "'" .
2188        $atts .
2189        ">\n" .
2190        "<div>" .
2191        "\n<input type='hidden' name='action' value='$script' />" .
2192        $corps .
2193        "</div></form>";
2194}
2195
2196/**
2197 * Créer une URL
2198 *
2199 * @param  string $script
2200 *     Nom du script à exécuter
2201 * @param  string $args
2202 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2203 * @param bool $no_entities
2204 *     Si false : transforme les & en &amp;
2205 * @param boolean $public
2206 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2207 *     true : l’URL sera relative.
2208 * @return string
2209 *     URL
2210 */
2211function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2212        // si l'on est dans l'espace prive, on garde dans l'url
2213        // l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2214        // ou non de proceder a l'authentification (cas typique de l'install par exemple)
2215        $url = (_DIR_RACINE and !$public)
2216                ? generer_url_ecrire(_request('exec'))
2217                : generer_url_public('', '', false, false);
2218        $url = parametre_url($url, 'action', $script);
2219        if ($args) {
2220                $url .= quote_amp('&' . $args);
2221        }
2222
2223        if ($no_entities) {
2224                $url = str_replace('&amp;', '&', $url);
2225        }
2226
2227        return $url;
2228}
2229
2230
2231/**
2232 * Fonction d'initialisation groupée pour compatibilité ascendante
2233 *
2234 * @param string $pi Répertoire permanent inaccessible
2235 * @param string $pa Répertoire permanent accessible
2236 * @param string $ti Répertoire temporaire inaccessible
2237 * @param string $ta Répertoire temporaire accessible
2238 */
2239function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2240        spip_initialisation_core($pi, $pa, $ti, $ta);
2241        spip_initialisation_suite();
2242}
2243
2244/**
2245 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2246 *
2247 * Elle définit les répertoires et fichiers non partageables
2248 * et indique dans $test_dirs ceux devant être accessibles en écriture
2249 * mais ne touche pas à cette variable si elle est déjà définie
2250 * afin que mes_options.php puisse en spécifier d'autres.
2251 *
2252 * Elle définit ensuite les noms des fichiers et les droits.
2253 * Puis simule un register_global=on sécurisé.
2254 *
2255 * @param string $pi Répertoire permanent inaccessible
2256 * @param string $pa Répertoire permanent accessible
2257 * @param string $ti Répertoire temporaire inaccessible
2258 * @param string $ta Répertoire temporaire accessible
2259 */
2260function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2261        static $too_late = 0;
2262        if ($too_late++) {
2263                return;
2264        }
2265
2266        // Declaration des repertoires
2267
2268        // le nom du repertoire plugins/ activables/desactivables
2269        if (!defined('_DIR_PLUGINS')) {
2270                define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2271        }
2272
2273        // le nom du repertoire des extensions/ permanentes du core, toujours actives
2274        if (!defined('_DIR_PLUGINS_DIST')) {
2275                define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2276        }
2277
2278        // le nom du repertoire des librairies
2279        if (!defined('_DIR_LIB')) {
2280                define('_DIR_LIB', _DIR_RACINE . "lib/");
2281        }
2282
2283        if (!defined('_DIR_IMG')) {
2284                define('_DIR_IMG', $pa);
2285        }
2286        if (!defined('_DIR_LOGOS')) {
2287                define('_DIR_LOGOS', $pa);
2288        }
2289        if (!defined('_DIR_IMG_ICONES')) {
2290                define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2291        }
2292
2293        if (!defined('_DIR_DUMP')) {
2294                define('_DIR_DUMP', $ti . "dump/");
2295        }
2296        if (!defined('_DIR_SESSIONS')) {
2297                define('_DIR_SESSIONS', $ti . "sessions/");
2298        }
2299        if (!defined('_DIR_TRANSFERT')) {
2300                define('_DIR_TRANSFERT', $ti . "upload/");
2301        }
2302        if (!defined('_DIR_CACHE')) {
2303                define('_DIR_CACHE', $ti . "cache/");
2304        }
2305        if (!defined('_DIR_CACHE_XML')) {
2306                define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2307        }
2308        if (!defined('_DIR_SKELS')) {
2309                define('_DIR_SKELS', _DIR_CACHE . "skel/");
2310        }
2311        if (!defined('_DIR_AIDE')) {
2312                define('_DIR_AIDE', _DIR_CACHE . "aide/");
2313        }
2314        if (!defined('_DIR_TMP')) {
2315                define('_DIR_TMP', $ti);
2316        }
2317
2318        if (!defined('_DIR_VAR')) {
2319                define('_DIR_VAR', $ta);
2320        }
2321
2322        if (!defined('_DIR_ETC')) {
2323                define('_DIR_ETC', $pi);
2324        }
2325        if (!defined('_DIR_CONNECT')) {
2326                define('_DIR_CONNECT', $pi);
2327        }
2328        if (!defined('_DIR_CHMOD')) {
2329                define('_DIR_CHMOD', $pi);
2330        }
2331
2332        if (!isset($GLOBALS['test_dirs']))
2333                // Pas $pi car il est bon de le mettre hors ecriture apres intstall
2334                // il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2335        {
2336                $GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2337        }
2338
2339        // Declaration des fichiers
2340
2341        if (!defined('_CACHE_PLUGINS_PATH')) {
2342                define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2343        }
2344        if (!defined('_CACHE_PLUGINS_OPT')) {
2345                define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2346        }
2347        if (!defined('_CACHE_PLUGINS_FCT')) {
2348                define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2349        }
2350        if (!defined('_CACHE_PIPELINES')) {
2351                define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2352        }
2353        if (!defined('_CACHE_CHEMIN')) {
2354                define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2355        }
2356
2357        # attention .php obligatoire pour ecrire_fichier_securise
2358        if (!defined('_FILE_META')) {
2359                define('_FILE_META', $ti . 'meta_cache.php');
2360        }
2361        if (!defined('_DIR_LOG')) {
2362                define('_DIR_LOG', _DIR_TMP . 'log/');
2363        }
2364        if (!defined('_FILE_LOG')) {
2365                define('_FILE_LOG', 'spip');
2366        }
2367        if (!defined('_FILE_LOG_SUFFIX')) {
2368                define('_FILE_LOG_SUFFIX', '.log');
2369        }
2370
2371        // Le fichier de connexion a la base de donnees
2372        // tient compte des anciennes versions (inc_connect...)
2373        if (!defined('_FILE_CONNECT_INS')) {
2374                define('_FILE_CONNECT_INS', 'connect');
2375        }
2376        if (!defined('_FILE_CONNECT')) {
2377                define('_FILE_CONNECT',
2378                (@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2379                        : (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2380                                : false)));
2381        }
2382
2383        // Le fichier de reglages des droits
2384        if (!defined('_FILE_CHMOD_INS')) {
2385                define('_FILE_CHMOD_INS', 'chmod');
2386        }
2387        if (!defined('_FILE_CHMOD')) {
2388                define('_FILE_CHMOD',
2389                (@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2390                        : false));
2391        }
2392
2393        if (!defined('_FILE_LDAP')) {
2394                define('_FILE_LDAP', 'ldap.php');
2395        }
2396
2397        if (!defined('_FILE_TMP_SUFFIX')) {
2398                define('_FILE_TMP_SUFFIX', '.tmp.php');
2399        }
2400        if (!defined('_FILE_CONNECT_TMP')) {
2401                define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2402        }
2403        if (!defined('_FILE_CHMOD_TMP')) {
2404                define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2405        }
2406
2407        // Definition des droits d'acces en ecriture
2408        if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2409                include_once _FILE_CHMOD;
2410        }
2411
2412        // Se mefier des fichiers mal remplis!
2413        if (!defined('_SPIP_CHMOD')) {
2414                define('_SPIP_CHMOD', 0777);
2415        }
2416
2417        if (!defined('_DEFAULT_CHARSET')) {
2418                /** Le charset par défaut lors de l'installation */
2419                define('_DEFAULT_CHARSET', 'utf-8');
2420        }
2421        if (!defined('_ROOT_PLUGINS')) {
2422                define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2423        }
2424        if (!defined('_ROOT_PLUGINS_DIST')) {
2425                define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2426        }
2427        if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2428                define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2429        }
2430
2431        // La taille des Log
2432        if (!defined('_MAX_LOG')) {
2433                define('_MAX_LOG', 100);
2434        }
2435
2436        // Sommes-nous dans l'empire du Mal ?
2437        // (ou sous le signe du Pingouin, ascendant GNU ?)
2438        if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2439                if (!defined('_OS_SERVEUR')) {
2440                        define('_OS_SERVEUR', 'windows');
2441                }
2442                if (!defined('_SPIP_LOCK_MODE')) {
2443                        define('_SPIP_LOCK_MODE', 1);
2444                } // utiliser le flock php
2445        } else {
2446                if (!defined('_OS_SERVEUR')) {
2447                        define('_OS_SERVEUR', '');
2448                }
2449                if (!defined('_SPIP_LOCK_MODE')) {
2450                        define('_SPIP_LOCK_MODE', 1);
2451                } // utiliser le flock php
2452                #if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2453        }
2454
2455        // Langue par defaut
2456        if (!defined('_LANGUE_PAR_DEFAUT')) {
2457                define('_LANGUE_PAR_DEFAUT', 'fr');
2458        }
2459
2460        //
2461        // Module de lecture/ecriture/suppression de fichiers utilisant flock()
2462        // (non surchargeable en l'etat ; attention si on utilise include_spip()
2463        // pour le rendre surchargeable, on va provoquer un reecriture
2464        // systematique du noyau ou une baisse de perfs => a etudier)
2465        include_once _ROOT_RESTREINT . 'inc/flock.php';
2466
2467        // charger tout de suite le path et son cache
2468        load_path_cache();
2469
2470        // *********** traiter les variables ************
2471
2472        //
2473        // Securite
2474        //
2475
2476        // Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2477        if (isset($_REQUEST['GLOBALS'])) {
2478                die();
2479        }
2480        // nettoyer les magic quotes \' et les caracteres nuls %00
2481        spip_desinfecte($_GET);
2482        spip_desinfecte($_POST);
2483        spip_desinfecte($_COOKIE);
2484        spip_desinfecte($_REQUEST);
2485
2486        // Si les variables sont passees en global par le serveur,
2487        // il faut faire quelques verifications de base
2488        // Todo: test à supprimer lorsque version PHP minimum >= 5.4.
2489        $avertir_register_globals = false;
2490        if (test_valeur_serveur(@ini_get('register_globals'))) {
2491                // ne pas desinfecter les globales en profondeur car elle contient aussi les
2492                // precedentes, qui seraient desinfectees 2 fois.
2493                spip_desinfecte($GLOBALS, false);
2494                // plugin grenier
2495                if (include_spip('inc/php3')) {
2496                        spip_register_globals(true);
2497                }
2498
2499                $avertir_register_globals = true;
2500        }
2501
2502        // appliquer le cookie_prefix
2503        if ($GLOBALS['cookie_prefix'] != 'spip') {
2504                include_spip('inc/cookie');
2505                recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2506        }
2507
2508        //
2509        // Capacites php (en fonction de la version)
2510        //
2511        $GLOBALS['flag_ob'] = (function_exists("ob_start")
2512                && function_exists("ini_get")
2513                && !strstr(@ini_get('disable_functions'), 'ob_'));
2514        $GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2515        $GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2516        $GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2517                (get_cfg_var('upload_max_filesize') > 0));
2518
2519
2520        // Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2521        if (isset($_SERVER['REQUEST_URI'])) {
2522                $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2523        } else {
2524                $GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2525                if (!empty($_SERVER['QUERY_STRING'])
2526                        and !strpos($_SERVER['REQUEST_URI'], '?')
2527                ) {
2528                        $GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2529                }
2530        }
2531
2532        // Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2533        if (!defined('_RENOUVELLE_ALEA')) {
2534                define('_RENOUVELLE_ALEA', 12 * 3600);
2535        }
2536        if (!defined('_DUREE_COOKIE_ADMIN')) {
2537                define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2538        }
2539
2540        // charger les meta si possible et renouveller l'alea au besoin
2541        // charge aussi effacer_meta et ecrire_meta
2542        $inc_meta = charger_fonction('meta', 'inc');
2543        $inc_meta();
2544
2545        // on a pas pu le faire plus tot
2546        if ($avertir_register_globals) {
2547                avertir_auteurs("register_globals",
2548                        _L("Probl&egrave;me de s&eacute;curit&eacute; : register_globals=on; dans php.ini &agrave; corriger."));
2549        }
2550
2551        // nombre de repertoires depuis la racine
2552        // on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2553        // ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2554        // le calcul est faux)
2555        if (!_DIR_RESTREINT) {
2556                $GLOBALS['profondeur_url'] = 1;
2557        } else {
2558                $uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2559                $uri_ref = $_SERVER["SCRIPT_NAME"];
2560                if (!$uri_ref
2561                        // si on est appele avec un autre ti, on est sans doute en mutu
2562                        // si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2563                        // a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2564                        // s'en remettre a l'adresse du site. alea jacta est.
2565                        or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2566                ) {
2567
2568                        if (isset($GLOBALS['meta']['adresse_site'])) {
2569                                $uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2570                                $uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2571                        } else {
2572                                $uri_ref = "";
2573                        }
2574                }
2575                if (!$uri or !$uri_ref) {
2576                        $GLOBALS['profondeur_url'] = 0;
2577                } else {
2578                        $GLOBALS['profondeur_url'] = max(0,
2579                                substr_count($uri[0], '/')
2580                                - substr_count($uri_ref, '/'));
2581                }
2582        }
2583        // s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2584        if (_FILE_CONNECT) {
2585                if (verifier_visiteur() == '0minirezo'
2586                        // si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2587                        and !isset($_COOKIE['spip_admin'])
2588                ) {
2589                        clear_path_cache();
2590                }
2591        }
2592
2593}
2594
2595/**
2596 * Complements d'initialisation non critiques pouvant etre realises
2597 * par les plugins
2598 *
2599 */
2600function spip_initialisation_suite() {
2601        static $too_late = 0;
2602        if ($too_late++) {
2603                return;
2604        }
2605
2606        // taille mini des login
2607        if (!defined('_LOGIN_TROP_COURT')) {
2608                define('_LOGIN_TROP_COURT', 4);
2609        }
2610
2611        // la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2612        #if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2613        #if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2614        #if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2615
2616        // la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2617        #if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2618        #if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2619        #if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2620        #if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2621
2622        if (!defined('_PASS_LONGUEUR_MINI')) {
2623                define('_PASS_LONGUEUR_MINI', 6);
2624        }
2625
2626
2627        // Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2628        if (!defined('_IMG_QUALITE')) {
2629                define('_IMG_QUALITE', 85);
2630        } # valeur par defaut
2631        if (!defined('_IMG_GD_QUALITE')) {
2632                define('_IMG_GD_QUALITE', _IMG_QUALITE);
2633        } # surcharge pour la lib GD
2634        if (!defined('_IMG_CONVERT_QUALITE')) {
2635                define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2636        } # surcharge pour imagick en ligne de commande
2637        // Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2638        if (!defined('_IMG_IMAGICK_QUALITE')) {
2639                define('_IMG_IMAGICK_QUALITE', 75);
2640        } # surcharge pour imagick en PHP
2641
2642        if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2643                define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2644        } // poids en octet
2645
2646        // qq chaines standard
2647        if (!defined('_ACCESS_FILE_NAME')) {
2648                define('_ACCESS_FILE_NAME', '.htaccess');
2649        }
2650        if (!defined('_AUTH_USER_FILE')) {
2651                define('_AUTH_USER_FILE', '.htpasswd');
2652        }
2653        if (!defined('_SPIP_DUMP')) {
2654                define('_SPIP_DUMP', 'dump@nom_site@@stamp@.xml');
2655        }
2656        if (!defined('_CACHE_RUBRIQUES')) {
2657                /** Fichier cache pour le navigateur de rubrique du bandeau */
2658                define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2659        }
2660        if (!defined('_CACHE_RUBRIQUES_MAX')) {
2661                /** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2662                define('_CACHE_RUBRIQUES_MAX', 500);
2663        }
2664
2665        if (!defined('_EXTENSION_SQUELETTES')) {
2666                define('_EXTENSION_SQUELETTES', 'html');
2667        }
2668
2669        if (!defined('_DOCTYPE_ECRIRE')) {
2670                define('_DOCTYPE_ECRIRE',
2671                        // "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
2672                        //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
2673                        //"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
2674                        // "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1 //EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>\n");
2675                "<!DOCTYPE html>\n");
2676        }
2677        if (!defined('_DOCTYPE_AIDE')) {
2678                define('_DOCTYPE_AIDE',
2679                "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2680        }
2681
2682        /** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2683         * le script de l'espace public, alias index.php */
2684        if (!defined('_SPIP_SCRIPT')) {
2685                define('_SPIP_SCRIPT', 'spip.php');
2686        }
2687        /** Argument page, personalisable en cas de conflit avec un autre script */
2688        if (!defined('_SPIP_PAGE')) {
2689                define('_SPIP_PAGE', 'page');
2690        }
2691
2692        // le script de l'espace prive
2693        // Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2694        // les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2695        // meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2696        if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2697                define('_SPIP_ECRIRE_SCRIPT', (empty($_SERVER['SERVER_SOFTWARE']) ? '' :
2698                        preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE']) ?
2699                                'index.php' : ''));
2700        }
2701
2702
2703        if (!defined('_SPIP_AJAX')) {
2704                define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2705                        ? 1
2706                        : (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2707        }
2708
2709        // La requete est-elle en ajax ?
2710        if (!defined('_AJAX')) {
2711                define('_AJAX',
2712                        (isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2713                                or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2714                                or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2715                                or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2716                        )
2717                        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
2718                );
2719        }
2720
2721        # nombre de pixels maxi pour calcul de la vignette avec gd
2722        # au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2723        # les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2724        if (!defined('_IMG_GD_MAX_PIXELS')) {
2725                define('_IMG_GD_MAX_PIXELS',
2726                (isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2727                        ? $GLOBALS['meta']['max_taille_vignettes']
2728                        : 0);
2729        }
2730
2731        if (!defined('_MEMORY_LIMIT_MIN')) {
2732                define('_MEMORY_LIMIT_MIN', 16);
2733        } // en Mo
2734        // si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2735        // on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2736        // il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2737        if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2738                if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2739                        $unit = strtolower(substr($memory, -1));
2740                        $memory = substr($memory, 0, -1);
2741                        switch ($unit) {
2742                                // Le modifieur 'G' est disponible depuis PHP 5.1.0
2743                                case 'g':
2744                                        $memory *= 1024;
2745                                case 'm':
2746                                        $memory *= 1024;
2747                                case 'k':
2748                                        $memory *= 1024;
2749                        }
2750                        if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2751                                @ini_set('memory_limit', $m = _MEMORY_LIMIT_MIN . 'M');
2752                                if (trim(ini_get('memory_limit')) != $m) {
2753                                        if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2754                                                define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2755                                        } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2756                                }
2757                        }
2758                } else {
2759                        if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2760                                define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2761                        }
2762                } // evite une page blanche car on ne saura pas calculer la css dans ce hit
2763        }
2764        // Protocoles a normaliser dans les chaines de langues
2765        if (!defined('_PROTOCOLES_STD')) {
2766                define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2767        }
2768
2769        init_var_mode();
2770}
2771
2772/**
2773 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2774 * la validité du cache ou certains affichages spéciaux.
2775 *
2776 * Le paramètre d'URL `var_mode` permet de
2777 * modifier la pérennité du cache, recalculer des urls
2778 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2779 * d'afficher un écran de débug ou des traductions non réalisées.
2780 *
2781 * En fonction de ces paramètres dans l'URL appelante, on définit
2782 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2783 *
2784 * Le paramètre `var_mode` accepte ces valeurs :
2785 *
2786 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2787 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2788 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2789 * - `debug` :  modifie l'affichage activant le mode "debug"
2790 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2791 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2792 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2793 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2794 *
2795 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2796 *
2797 * @note
2798 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2799 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2800 * `   var_mode` (calcul ou recalcul).
2801 */
2802function init_var_mode() {
2803        static $done = false;
2804        if (!$done) {
2805
2806                if (isset($_GET['var_mode'])) {
2807                        $var_mode = explode(',', $_GET['var_mode']);
2808                        // tout le monde peut calcul/recalcul
2809                        if (!defined('_VAR_MODE')) {
2810                                if (in_array('recalcul', $var_mode)) {
2811                                        define('_VAR_MODE', 'recalcul');
2812                                } elseif (in_array('calcul', $var_mode)) {
2813                                        define('_VAR_MODE', 'calcul');
2814                                }
2815                        }
2816                        $var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2817                        if ($var_mode) {
2818                                include_spip('inc/autoriser');
2819                                // autoriser preview si preview seulement, et sinon autoriser debug
2820                                if (autoriser(
2821                                        ($_GET['var_mode'] == 'preview')
2822                                                ? 'previsualiser'
2823                                                : 'debug'
2824                                )) {
2825                                        if (in_array('traduction', $var_mode)) {
2826                                                // forcer le calcul pour passer dans traduire
2827                                                if (!defined('_VAR_MODE')) {
2828                                                        define('_VAR_MODE', 'calcul');
2829                                                }
2830                                                // et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2831                                                if (!defined('_VAR_NOCACHE')) {
2832                                                        define('_VAR_NOCACHE', true);
2833                                                }
2834                                                $var_mode = array_diff($var_mode, array('traduction'));
2835                                        }
2836                                        if (in_array('preview', $var_mode)) {
2837                                                // basculer sur les criteres de preview dans les boucles
2838                                                if (!defined('_VAR_PREVIEW')) {
2839                                                        define('_VAR_PREVIEW', true);
2840                                                }
2841                                                // forcer le calcul
2842                                                if (!defined('_VAR_MODE')) {
2843                                                        define('_VAR_MODE', 'calcul');
2844                                                }
2845                                                // et ne pas enregistrer de cache
2846                                                if (!defined('_VAR_NOCACHE')) {
2847                                                        define('_VAR_NOCACHE', true);
2848                                                }
2849                                                $var_mode = array_diff($var_mode, array('preview'));
2850                                        }
2851                                        if (in_array('inclure', $var_mode)) {
2852                                                // forcer le compilo et ignorer les caches existants
2853                                                if (!defined('_VAR_MODE')) {
2854                                                        define('_VAR_MODE', 'calcul');
2855                                                }
2856                                                if (!defined('_VAR_INCLURE')) {
2857                                                        define('_VAR_INCLURE', true);
2858                                                }
2859                                                // et ne pas enregistrer de cache
2860                                                if (!defined('_VAR_NOCACHE')) {
2861                                                        define('_VAR_NOCACHE', true);
2862                                                }
2863                                                $var_mode = array_diff($var_mode, array('inclure'));
2864                                        }
2865                                        if (in_array('urls', $var_mode)) {
2866                                                // forcer le compilo et ignorer les caches existants
2867                                                if (!defined('_VAR_MODE')) {
2868                                                        define('_VAR_MODE', 'calcul');
2869                                                }
2870                                                if (!defined('_VAR_URLS')) {
2871                                                        define('_VAR_URLS', true);
2872                                                }
2873                                                $var_mode = array_diff($var_mode, array('urls'));
2874                                        }
2875                                        if (in_array('images', $var_mode)) {
2876                                                // forcer le compilo et ignorer les caches existants
2877                                                if (!defined('_VAR_MODE')) {
2878                                                        define('_VAR_MODE', 'calcul');
2879                                                }
2880                                                // indiquer qu'on doit recalculer les images
2881                                                if (!defined('_VAR_IMAGES')) {
2882                                                        define('_VAR_IMAGES', true);
2883                                                }
2884                                                $var_mode = array_diff($var_mode, array('images'));
2885                                        }
2886                                        if (in_array('debug', $var_mode)) {
2887                                                if (!defined('_VAR_MODE')) {
2888                                                        define('_VAR_MODE', 'debug');
2889                                                }
2890                                                // et ne pas enregistrer de cache
2891                                                if (!defined('_VAR_NOCACHE')) {
2892                                                        define('_VAR_NOCACHE', true);
2893                                                }
2894                                                $var_mode = array_diff($var_mode, array('debug'));
2895                                        }
2896                                        if (count($var_mode) and !defined('_VAR_MODE')) {
2897                                                define('_VAR_MODE', reset($var_mode));
2898                                        }
2899                                        if (isset($GLOBALS['visiteur_session']['nom'])) {
2900                                                spip_log($GLOBALS['visiteur_session']['nom']
2901                                                        . " " . _VAR_MODE);
2902                                        }
2903                                } // pas autorise ?
2904                                else {
2905                                        // si on n'est pas connecte on se redirige
2906                                        if (!$GLOBALS['visiteur_session']) {
2907                                                include_spip('inc/headers');
2908                                                redirige_par_entete(generer_url_public('login',
2909                                                        'url=' . rawurlencode(
2910                                                                parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2911                                                        ), true));
2912                                        }
2913                                        // sinon tant pis
2914                                }
2915                        }
2916                }
2917                if (!defined('_VAR_MODE')) {
2918                        /**
2919                         * Indique le mode de calcul ou d'affichage de la page.
2920                         * @see init_var_mode()
2921                         */
2922                        define('_VAR_MODE', false);
2923                }
2924                $done = true;
2925        }
2926}
2927
2928// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2929// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2930// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2931// http://code.spip.net/@spip_desinfecte
2932function spip_desinfecte(&$t, $deep = true) {
2933        static $magic_quotes;
2934        if (!isset($magic_quotes)) {
2935                $magic_quotes = @get_magic_quotes_gpc();
2936        }
2937
2938        foreach ($t as $key => $val) {
2939                if (is_string($t[$key])) {
2940                        if ($magic_quotes) {
2941                                $t[$key] = stripslashes($t[$key]);
2942                        }
2943                        $t[$key] = str_replace(chr(0), '-', $t[$key]);
2944                } // traiter aussi les "texte_plus" de article_edit
2945                else {
2946                        if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2947                                spip_desinfecte($t[$key], $deep);
2948                        }
2949                }
2950        }
2951}
2952
2953//  retourne le statut du visiteur s'il s'annonce
2954
2955// http://code.spip.net/@verifier_visiteur
2956function verifier_visiteur() {
2957        // Rq: pour que cette fonction marche depuis mes_options
2958        // il faut forcer l'init si ce n'est fait
2959        // mais on risque de perturber des plugins en initialisant trop tot
2960        // certaines constantes
2961        @spip_initialisation_core(
2962                (_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2963                (_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2964                (_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2965                (_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2966        );
2967
2968        // Demarrer une session NON AUTHENTIFIEE si on donne son nom
2969        // dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2970        // Attention on separe bien session_nom et nom, pour eviter
2971        // les melanges entre donnees SQL et variables plus aleatoires
2972        $variables_session = array('session_nom', 'session_email');
2973        foreach ($variables_session as $var) {
2974                if (_request($var) !== null) {
2975                        $init = true;
2976                        break;
2977                }
2978        }
2979        if (isset($init)) {
2980                #@spip_initialisation_suite();
2981                $session = charger_fonction('session', 'inc');
2982                $session();
2983                include_spip('inc/texte');
2984                foreach ($variables_session as $var) {
2985                        if (($a = _request($var)) !== null) {
2986                                $GLOBALS['visiteur_session'][$var] = safehtml($a);
2987                        }
2988                }
2989                if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
2990                        $GLOBALS['visiteur_session']['id_auteur'] = 0;
2991                }
2992                $session($GLOBALS['visiteur_session']);
2993
2994                return 0;
2995        }
2996
2997        $h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
2998        if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
2999
3000                $session = charger_fonction('session', 'inc');
3001                if ($session()) {
3002                        return $GLOBALS['visiteur_session']['statut'];
3003                }
3004                if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
3005                        include_spip('inc/auth');
3006                        $h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
3007                }
3008                if ($h) {
3009                        $GLOBALS['visiteur_session'] = $h;
3010
3011                        return $GLOBALS['visiteur_session']['statut'];
3012                }
3013        }
3014
3015        // au moins son navigateur nous dit la langue preferee de cet inconnu
3016        include_spip('inc/lang');
3017        utiliser_langue_visiteur();
3018
3019        return false;
3020}
3021
3022
3023/**
3024 * Sélectionne la langue donnée en argument et mémorise la courante
3025 *
3026 * Restaure l'ancienne langue si appellée sans argument.
3027 *
3028 * @note
3029 *     On pourrait économiser l'empilement en cas de non changemnt
3030 *     et lui faire retourner `False` pour prevenir l'appelant
3031 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3032 *     cette fonction retourne toujours non `False`
3033 *
3034 * @uses changer_langue()
3035 * @param null|string $lang
3036 *     - string : Langue à appliquer,
3037 *     - null : Pour restituer la dernière langue mémorisée.
3038 * @return string
3039 *     - string Langue utilisée.
3040 **/
3041function lang_select($lang = null) {
3042        static $pile_langues = array();
3043        if (!function_exists('changer_langue')) {
3044                include_spip('inc/lang');
3045        }
3046        if ($lang === null) {
3047                $lang = array_pop($pile_langues);
3048        } else {
3049                array_push($pile_langues, $GLOBALS['spip_lang']);
3050        }
3051        if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3052                return $lang;
3053        }
3054        changer_langue($lang);
3055
3056        return $lang;
3057}
3058
3059/**
3060 * Renvoie une chaîne qui identifie la session courante
3061 *
3062 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3063 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3064 * de fichier cache.
3065 *
3066 * @pipeline_appel definir_session
3067 *
3068 * @param bool $force
3069 * @return string
3070 *     Identifiant de la session
3071 **/
3072function spip_session($force = false) {
3073        static $session;
3074        if ($force or !isset($session)) {
3075                $s = pipeline('definir_session',
3076                        $GLOBALS['visiteur_session']
3077                                ? serialize($GLOBALS['visiteur_session'])
3078                                . '_' . @$_COOKIE['spip_session']
3079                                : ''
3080                );
3081                $session = $s ? substr(md5($s), 0, 8) : '';
3082        }
3083
3084        #spip_log('session: '.$session);
3085        return $session;
3086}
3087
3088
3089/**
3090 * Retourne un lien vers une aide
3091 *
3092 * Aide, aussi depuis l'espace privé à présent.
3093 * Surchargeable mais pas d'erreur fatale si indisponible.
3094 *
3095 * @param string $aide
3096 *    Cle d'identification de l'aide desiree
3097 * @param bool $distante
3098 *    Generer une url locale (par defaut)
3099 *    ou une url distante [directement sur spip.net]
3100 * @return
3101 *    Lien sur une icone d'aide
3102 **/
3103function aider($aide = '', $distante = false) {
3104        $aider = charger_fonction('aide', 'inc', true);
3105
3106        return $aider ? $aider($aide, '', array(), $distante) : '';
3107}
3108
3109/**
3110 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3111 *
3112 * Si l’utiliseur est un webmestre.
3113 */
3114function exec_info_dist() {
3115
3116        include_spip('inc/autoriser');
3117        if (autoriser('webmestre')) {
3118                phpinfo();
3119        } else {
3120                include_spip('inc/filtres');
3121                sinon_interdire_acces();
3122        }
3123}
3124
3125/**
3126 * Génère une erreur de squelette
3127 *
3128 * Génère une erreur de squelette qui sera bien visible par un
3129 * administrateur authentifié lors d'une visite de la page en erreur
3130 *
3131 * @param bool|string|array $message
3132 *     - Message d'erreur (string|array)
3133 *     - false pour retourner le texte des messages d'erreurs
3134 *     - vide pour afficher les messages d'erreurs
3135 * @param string|array|object $lieu
3136 *     Lieu d'origine de l'erreur
3137 * @return null|string
3138 *     - Rien dans la plupart des cas
3139 *     - string si $message à false.
3140 **/
3141function erreur_squelette($message = '', $lieu = '') {
3142        $debusquer = charger_fonction('debusquer', 'public');
3143        if (is_array($lieu)) {
3144                include_spip('public/compiler');
3145                $lieu = reconstruire_contexte_compil($lieu);
3146        }
3147
3148        return $debusquer($message, $lieu);
3149}
3150
3151/**
3152 * Calcule un squelette avec un contexte et retourne son contenu
3153 *
3154 * La fonction de base de SPIP : un squelette + un contexte => une page.
3155 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3156 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3157 * $options permet de selectionner les options suivantes :
3158 *
3159 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3160 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3161 *          pour chaque $fond fourni.
3162 *
3163 * @api
3164 * @param string /array $fond
3165 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3166 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3167 * @param array $contexte
3168 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3169 *     - La langue est transmise automatiquement (sauf option étoile).
3170 * @param array $options
3171 *     Options complémentaires :
3172 *
3173 *     - trim   : applique un trim sur le résultat (true par défaut)
3174 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3175 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3176 *                équivalent de INCLURE*
3177 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3178 * @param string $connect
3179 *     Non du connecteur de bdd a utiliser
3180 * @return string|array
3181 *     - Contenu du squelette calculé
3182 *     - ou tableau d'information sur le squelette.
3183 */
3184function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3185        if (!function_exists('evaluer_fond')) {
3186                include_spip('public/assembler');
3187        }
3188        // assurer la compat avec l'ancienne syntaxe
3189        // (trim etait le 3eme argument, par defaut a true)
3190        if (!is_array($options)) {
3191                $options = array('trim' => $options);
3192        }
3193        if (!isset($options['trim'])) {
3194                $options['trim'] = true;
3195        }
3196
3197        if (isset($contexte['connect'])) {
3198                $connect = $contexte['connect'];
3199                unset($contexte['connect']);
3200        }
3201
3202        $texte = "";
3203        $pages = array();
3204        $lang_select = '';
3205        if (!isset($options['etoile']) or !$options['etoile']) {
3206                // Si on a inclus sans fixer le critere de lang, on prend la langue courante
3207                if (!isset($contexte['lang'])) {
3208                        $contexte['lang'] = $GLOBALS['spip_lang'];
3209                }
3210
3211                if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3212                        $lang_select = lang_select($contexte['lang']);
3213                }
3214        }
3215
3216        if (!isset($GLOBALS['_INC_PUBLIC'])) {
3217                $GLOBALS['_INC_PUBLIC'] = 0;
3218        }
3219
3220        $GLOBALS['_INC_PUBLIC']++;
3221
3222
3223        // pour fix #4235
3224        // On mémorise l'état initial du sessionnement du contexte incluant
3225        $cache_utilise_session_appelant = (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
3226
3227        foreach (is_array($fond) ? $fond : array($fond) as $f) {
3228
3229                unset($GLOBALS['cache_utilise_session']); // fix #4235 : Chaque inclusion commence sans sessionnement préallable
3230
3231                $page = evaluer_fond($f, $contexte, $connect);
3232                if ($page === '') {
3233                        $c = isset($options['compil']) ? $options['compil'] : '';
3234                        $a = array('fichier' => $fond);
3235                        $erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3236                        erreur_squelette($erreur, $c);
3237                        // eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3238                        $page = array('texte' => '', 'erreur' => $erreur);
3239                }
3240
3241                $page = pipeline('recuperer_fond', array(
3242                        'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3243                        'data' => $page
3244                ));
3245                if (isset($options['ajax']) and $options['ajax']) {
3246                        if (!function_exists('encoder_contexte_ajax')) {
3247                                include_spip('inc/filtres');
3248                        }
3249                        $page['texte'] = encoder_contexte_ajax(
3250                                array_merge(
3251                                        $contexte,
3252                                        array('fond' => $f),
3253                                        ($connect ? array('connect' => $connect) : array())
3254                                ),
3255                                '',
3256                                $page['texte'],
3257                                $options['ajax']
3258                        );
3259                }
3260
3261                if (isset($options['raw']) and $options['raw']) {
3262                        $pages[] = $page;
3263                } else {
3264                        $texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3265                }
3266                // fix #4235 : contamination de la session appelante, pour les inclusions statiques
3267                if (isset($options['sessionnement_contaminant'])
3268                        and isset($page['invalideurs']['session']))
3269                        $cache_utilise_session_appelant = $page['invalideurs']['session'];
3270        }
3271
3272        // fix #4235 : restaurer le sessionnement du contexte appelant,
3273        // éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
3274        $GLOBALS['cache_utilise_session']
3275                = (isset($cache_utilise_session_appelant) ? $cache_utilise_session_appelant : null);
3276
3277        $GLOBALS['_INC_PUBLIC']--;
3278
3279        if ($lang_select) {
3280                lang_select();
3281        }
3282        if (isset($options['raw']) and $options['raw']) {
3283                return is_array($fond) ? $pages : reset($pages);
3284        } else {
3285                return $options['trim'] ? ltrim($texte) : $texte;
3286        }
3287}
3288
3289/**
3290 * Trouve un squelette dans le repertoire modeles/
3291 *
3292 * @param  $nom
3293 * @return string
3294 */
3295function trouve_modele($nom) {
3296        return trouver_fond($nom, 'modeles/');
3297}
3298
3299/**
3300 * Trouver un squelette dans le chemin
3301 * on peut specifier un sous-dossier dans $dir
3302 * si $pathinfo est a true, retourne un tableau avec
3303 * les composantes du fichier trouve
3304 * + le chemin complet sans son extension dans fond
3305 *
3306 * @param string $nom
3307 * @param string $dir
3308 * @param bool $pathinfo
3309 * @return array|string
3310 */
3311function trouver_fond($nom, $dir = '', $pathinfo = false) {
3312        $f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3313        if (!$pathinfo) {
3314                return $f;
3315        }
3316        // renvoyer un tableau detaille si $pathinfo==true
3317        $p = pathinfo($f);
3318        if (!isset($p['extension']) or !$p['extension']) {
3319                $p['extension'] = _EXTENSION_SQUELETTES;
3320        }
3321        if (!isset($p['extension']) or !$p['filename']) {
3322                $p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3323        }
3324        $p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3325
3326        return $p;
3327}
3328
3329/**
3330 * Teste, pour un nom de page de l'espace privé, s'il est possible
3331 * de générer son contenu.
3332 *
3333 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3334 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3335 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3336 *
3337 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3338 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3339 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3340 *
3341 * @param string $nom
3342 *     Nom de la page
3343 * @return string
3344 *     Nom de l'exec, sinon chaîne vide.
3345 **/
3346function tester_url_ecrire($nom) {
3347        static $exec = array();
3348        if (isset($exec[$nom])) {
3349                return $exec[$nom];
3350        }
3351        // tester si c'est une page en squelette
3352        if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3353                return $exec[$nom] = 'fond';
3354        } // compat skels orthogonaux version precedente
3355        elseif (trouver_fond($nom, 'prive/exec/')) {
3356                return $exec[$nom] = 'fond_monobloc';
3357        } // echafaudage d'un fond !
3358        elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3359                return $exec[$nom] = 'fond';
3360        }
3361        // attention, il ne faut pas inclure l'exec ici
3362        // car sinon #URL_ECRIRE provoque des inclusions
3363        // et des define intrusifs potentiels
3364        return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3365}
3366
3367
3368/**
3369 * Tente de charger dynamiquement une extension PHP
3370 *
3371 * @example
3372 *     ```
3373 *     $ok = charger_php_extension('sqlite');
3374 *     ```
3375 * @uses inc_charger_php_extension_dist() Si la librairie n'est pas déjà charchée
3376 *
3377 * @param string $module Nom du module à charger
3378 * @return bool true si le module est chargé
3379 **/
3380function charger_php_extension($module) {
3381        if (extension_loaded($module)) {
3382                return true;
3383        } else {
3384                $charger_php_extension = charger_fonction('charger_php_extension', 'inc');
3385
3386                return $charger_php_extension($module);
3387        }
3388}
3389
3390
3391/**
3392 * Indique si le code HTML5 est permis sur le site public
3393 *
3394 * @return bool
3395 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3396 **/
3397function html5_permis() {
3398        return (isset($GLOBALS['meta']['version_html_max'])
3399                and ('html5' == $GLOBALS['meta']['version_html_max']));
3400}
3401
3402/*
3403 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3404 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3405 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3406 */
3407
3408/**
3409 * lire_meta : fonction dépréciée
3410 *
3411 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3412 * @see lire_config()
3413 * @param string $nom Clé de meta à lire
3414 * @return mixed Valeur de la meta.
3415 **/
3416function lire_meta($nom) {
3417        return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3418}
3419
3420
3421/**
3422 * ecrire_metas : fonction dépréciée
3423 *
3424 * @deprecated
3425 **/
3426function ecrire_metas() { }
3427
3428/**
3429 * Retourne une ligne d'un résultat de requête mysql (déprécié)
3430 *
3431 * @see sql_fetch()
3432 * @deprecated Utiliser sql_fetch()
3433 * @param Ressource $r Ressource mysql
3434 * @param int|null $t Type de retour
3435 * @return array|void|bool Tableau de la ligne SQL
3436 **/
3437function spip_fetch_array($r, $t = null) {
3438        if (!isset($t)) {
3439                if ($r) {
3440                        return sql_fetch($r);
3441                }
3442        } else {
3443                if ($t == 'SPIP_NUM') {
3444                        $t = MYSQLI_NUM;
3445                }
3446                if ($t == 'SPIP_BOTH') {
3447                        $t = MYSQLI_BOTH;
3448                }
3449                if ($t == 'SPIP_ASSOC') {
3450                        $t = MYSQLI_ASSOC;
3451                }
3452                spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
3453                if ($r) {
3454                        return mysqli_fetch_array($r, $t);
3455                }
3456        }
3457}
3458
3459/**
3460 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3461 * au prochain passage dans l'espace prive
3462 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3463 * les alertes affichees une fois sont effacees
3464 *
3465 * @param string $nom
3466 * @param string $message
3467 * @param string $statut
3468 */
3469function avertir_auteurs($nom, $message, $statut = '') {
3470        $alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3471        if (!$alertes
3472                or !is_array($alertes = unserialize($alertes))
3473        ) {
3474                $alertes = array();
3475        }
3476
3477        if (!isset($alertes[$statut])) {
3478                $alertes[$statut] = array();
3479        }
3480        $alertes[$statut][$nom] = $message;
3481        ecrire_meta("message_alertes_auteurs", serialize($alertes));
3482}
3483
3484if (PHP_VERSION_ID < 50500) {
3485        if (!function_exists('array_column')) {
3486                /**
3487                 * Returns the values from a single column of the input array, identified by
3488                 * the $columnKey.
3489                 *
3490                 * Optionally, you may provide an $indexKey to index the values in the returned
3491                 * array by the values from the $indexKey column in the input array.
3492                 *
3493                 * @link http://php.net/manual/fr/function.array-column.php
3494                 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3495                 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3496                 * @license http://opensource.org/licenses/MIT MIT
3497                 *
3498                 * @param array $input A multi-dimensional array (record set) from which to pull
3499                 *                     a column of values.
3500                 * @param mixed $columnKey The column of values to return. This value may be the
3501                 *                         integer key of the column you wish to retrieve, or it
3502                 *                         may be the string key name for an associative array.
3503                 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3504                 *                        the returned array. This value may be the integer key
3505                 *                        of the column, or it may be the string key name.
3506                 * @return array
3507                 */
3508                function array_column($input = null, $columnKey = null, $indexKey = null)
3509                {
3510                        // Using func_get_args() in order to check for proper number of
3511                        // parameters and trigger errors exactly as the built-in array_column()
3512                        // does in PHP 5.5.
3513                        $argc = func_num_args();
3514                        $params = func_get_args();
3515
3516                        if ($argc < 2) {
3517                                trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3518                                return null;
3519                        }
3520
3521                        if (!is_array($params[0])) {
3522                                trigger_error(
3523                                        'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3524                                        E_USER_WARNING
3525                                );
3526                                return null;
3527                        }
3528
3529                        if (!is_int($params[1])
3530                                && !is_float($params[1])
3531                                && !is_string($params[1])
3532                                && $params[1] !== null
3533                                && !(is_object($params[1]) && method_exists($params[1], '__toString'))
3534                        ) {
3535                                trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3536                                return false;
3537                        }
3538
3539                        if (isset($params[2])
3540                                && !is_int($params[2])
3541                                && !is_float($params[2])
3542                                && !is_string($params[2])
3543                                && !(is_object($params[2]) && method_exists($params[2], '__toString'))
3544                        ) {
3545                                trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3546                                return false;
3547                        }
3548
3549                        $paramsInput = $params[0];
3550                        $paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3551
3552                        $paramsIndexKey = null;
3553                        if (isset($params[2])) {
3554                                if (is_float($params[2]) || is_int($params[2])) {
3555                                        $paramsIndexKey = (int) $params[2];
3556                                } else {
3557                                        $paramsIndexKey = (string) $params[2];
3558                                }
3559                        }
3560
3561                        $resultArray = array();
3562
3563                        foreach ($paramsInput as $row) {
3564                                $key = $value = null;
3565                                $keySet = $valueSet = false;
3566
3567                                if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3568                                        $keySet = true;
3569                                        $key = (string) $row[$paramsIndexKey];
3570                                }
3571
3572                                if ($paramsColumnKey === null) {
3573                                        $valueSet = true;
3574                                        $value = $row;
3575                                } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3576                                        $valueSet = true;
3577                                        $value = $row[$paramsColumnKey];
3578                                }
3579
3580                                if ($valueSet) {
3581                                        if ($keySet) {
3582                                                $resultArray[$key] = $value;
3583                                        } else {
3584                                                $resultArray[] = $value;
3585                                        }
3586                                }
3587
3588                        }
3589
3590                        return $resultArray;
3591                }
3592
3593        }
3594}
Note: See TracBrowser for help on using the repository browser.