545a9a7104b1563f8940bf560b243dd0176d415f
[lhc/web/www.git] / www / ecrire / inc / config.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2017 *
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 * Fonctions utilitaires pour le stockage et lecture de configuration
15 *
16 * @package SPIP\Core\Config
17 **/
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23
24 /**
25 * Appliquer les valeurs par défaut pour les options non initialisées
26 * (pour les langues c'est fait)
27 *
28 * @return void
29 */
30 function inc_config_dist() {
31 actualise_metas(liste_metas());
32 }
33
34 /**
35 * Expliquer une clé de configuration
36 *
37 * Analyser la clé de configuration pour déterminer
38 * - la table (ex: spip_metas),
39 * - le casier, la clé principale (ex: dada)
40 * - et le sous-casier éventuel, chemin dans la clé principale (ex: truc/muche)
41 *
42 * @param string $cfg
43 * Clé de configuration, tel que 'dada/truc/muche'
44 * @return array
45 * Liste (table, casier, sous_casier)
46 */
47 function expliquer_config($cfg) {
48 // par defaut, sur la table des meta
49 $table = 'meta';
50 $casier = null;
51 $sous_casier = array();
52 $cfg = explode('/', $cfg);
53
54 // si le premier argument est vide, c'est une syntaxe /table/ ou un appel vide ''
55 if (!reset($cfg) and count($cfg) > 1) {
56 array_shift($cfg);
57 $table = array_shift($cfg);
58 if (!isset($GLOBALS[$table])) {
59 lire_metas($table);
60 }
61 }
62
63 // si on a demande #CONFIG{/meta,'',0}
64 if (count($cfg)) {
65 // pas sur un appel vide ''
66 if ('' !== ($c = array_shift($cfg))) {
67 $casier = $c;
68 }
69 }
70
71 if (count($cfg)) {
72 $sous_casier = $cfg;
73 }
74
75 return array($table, $casier, $sous_casier);
76 }
77
78 /**
79 * Lecture de la configuration
80 *
81 * lire_config() permet de recuperer une config depuis le php<br>
82 * memes arguments que la balise (forcement)<br>
83 * $cfg: la config, lire_config('montruc') est un tableau<br>
84 * lire_config('/table/champ') lit le valeur de champ dans la table des meta 'spip_table'<br>
85 * lire_config('montruc/sub') est l'element "sub" de cette config equivalent a lire_config('/meta/montruc/sub')<br>
86 *
87 * lire_config('methode::montruc/sub') delegue la lecture a methode_lire_config_dist via un charger_fonction
88 * permet de brancher CFG ou autre outil externe qui etend les methodes de stockage de config
89 *
90 * $unserialize est mis par l'histoire
91 *
92 * @param string $cfg
93 * Clé de configuration
94 * @param mixed $def
95 * Valeur par défaut
96 * @param boolean $unserialize
97 * N'affecte que le dépôt 'meta' :
98 * True pour désérialiser automatiquement la valeur
99 * @return mixed
100 * Contenu de la configuration obtenue
101 */
102 function lire_config($cfg = '', $def = null, $unserialize = true) {
103 // lire le stockage sous la forme /table/valeur
104 // ou valeur qui est en fait implicitement /meta/valeur
105 // ou casier/valeur qui est en fait implicitement /meta/casier/valeur
106
107 // traiter en priorite le cas simple et frequent
108 // de lecture direct $GLOBALS['meta']['truc'], si $cfg ne contient ni / ni :
109 if ($cfg and strpbrk($cfg, '/:') === false) {
110 $r = isset($GLOBALS['meta'][$cfg]) ?
111 ((!$unserialize
112 // ne pas essayer de deserialiser autre chose qu'une chaine
113 or !is_string($GLOBALS['meta'][$cfg])
114 // ne pas essayer de deserialiser si ce n'est visiblement pas une chaine serializee
115 or strpos($GLOBALS['meta'][$cfg], ':') === false
116 or ($t = @unserialize($GLOBALS['meta'][$cfg])) === false) ? $GLOBALS['meta'][$cfg] : $t)
117 : $def;
118
119 return $r;
120 }
121
122 // Brancher sur methodes externes si besoin
123 if ($cfg and $p = strpos($cfg, '::')) {
124 $methode = substr($cfg, 0, $p);
125 $lire_config = charger_fonction($methode, 'lire_config');
126
127 return $lire_config(substr($cfg, $p + 2), $def, $unserialize);
128 }
129
130 list($table, $casier, $sous_casier) = expliquer_config($cfg);
131
132 if (!isset($GLOBALS[$table])) {
133 return $def;
134 }
135
136 $r = $GLOBALS[$table];
137
138 // si on a demande #CONFIG{/meta,'',0}
139 if (!$casier) {
140 return $unserialize ? $r : serialize($r);
141 }
142
143 // casier principal :
144 // le deserializer si demande
145 // ou si on a besoin
146 // d'un sous casier
147 $r = isset($r[$casier]) ? $r[$casier] : null;
148 if (($unserialize or count($sous_casier)) and $r and is_string($r)) {
149 $r = (($t = @unserialize($r)) === false ? $r : $t);
150 }
151
152 // aller chercher le sous_casier
153 while (!is_null($r) and $casier = array_shift($sous_casier)) {
154 $r = isset($r[$casier]) ? $r[$casier] : null;
155 }
156
157 if (is_null($r)) {
158 return $def;
159 }
160
161 return $r;
162 }
163
164 /**
165 * metapack est inclue dans lire_config, mais on traite le cas ou il est explicite
166 * metapack:: dans le $cfg de lire_config.
167 * On renvoie simplement sur lire_config
168 *
169 * @param string $cfg
170 * @param mixed $def
171 * @param bool $unserialize
172 * @return mixed
173 */
174 function lire_config_metapack_dist($cfg = '', $def = null, $unserialize = true) {
175 return lire_config($cfg, $def, $unserialize);
176 }
177
178
179 /**
180 * Ecrire une configuration
181 *
182 * @param string $cfg
183 * @param mixed $store
184 * @return bool
185 */
186 function ecrire_config($cfg, $store) {
187 // Brancher sur methodes externes si besoin
188 if ($cfg and $p = strpos($cfg, '::')) {
189 $methode = substr($cfg, 0, $p);
190 $ecrire_config = charger_fonction($methode, 'ecrire_config');
191
192 return $ecrire_config(substr($cfg, $p + 2), $store);
193 }
194
195 list($table, $casier, $sous_casier) = expliquer_config($cfg);
196 // il faut au moins un casier pour ecrire
197 if (!$casier) {
198 return false;
199 }
200
201 // trouvons ou creons le pointeur sur le casier
202 $st = isset($GLOBALS[$table][$casier]) ? $GLOBALS[$table][$casier] : null;
203 if (!is_array($st) and ($sous_casier or is_array($store))) {
204 $st = unserialize($st);
205 if ($st === false) {
206 // ne rien creer si c'est une demande d'effacement
207 if (is_null($store)) {
208 return false;
209 }
210 $st = array();
211 }
212 }
213
214 // si on a affaire a un sous caiser
215 // il faut ecrire au bon endroit sans perdre les autres sous casier freres
216 if ($c = $sous_casier) {
217 $sc = &$st;
218 $pointeurs = array();
219 while (count($c) and $cc = array_shift($c)) {
220 // creer l'entree si elle n'existe pas
221 if (!isset($sc[$cc])) {
222 // si on essaye d'effacer une config qui n'existe pas
223 // ne rien creer mais sortir
224 if (is_null($store)) {
225 return false;
226 }
227 $sc[$cc] = array();
228 }
229 $pointeurs[$cc] = &$sc;
230 $sc = &$sc[$cc];
231 }
232
233 // si c'est une demande d'effacement
234 if (is_null($store)) {
235 $c = $sous_casier;
236 $sous = array_pop($c);
237 // effacer, et remonter pour effacer les parents vides
238 do {
239 unset($pointeurs[$sous][$sous]);
240 } while ($sous = array_pop($c) and !count($pointeurs[$sous][$sous]));
241
242 // si on a vide tous les sous casiers,
243 // et que le casier est vide
244 // vider aussi la meta
245 if (!$sous and !count($st)) {
246 $st = null;
247 }
248 } // dans tous les autres cas, on ecrase
249 else {
250 $sc = $store;
251 }
252
253 // Maintenant que $st est modifiee
254 // reprenons la comme valeur a stocker dans le casier principal
255 $store = $st;
256 }
257
258 if (is_null($store)) {
259 if (is_null($st) and !$sous_casier) {
260 return false;
261 } // la config n'existait deja pas !
262 effacer_meta($casier, $table);
263 if (!count($GLOBALS[$table])
264 or count($GLOBALS[$table]) == 1 and isset($GLOBALS[$table]['charset'])
265 ) {
266 effacer_meta('charset', $table); // enlevons cette meta
267 supprimer_table_meta($table); // supprimons la table (si elle est bien vide)
268 }
269 } // les meta ne peuvent etre que des chaines : il faut serializer le reste
270 else {
271 if (!isset($GLOBALS[$table])) {
272 installer_table_meta($table);
273 }
274 // si ce n'est pas une chaine
275 // il faut serializer
276 if (!is_string($store)) {
277 $store = serialize($store);
278 }
279 ecrire_meta($casier, $store, null, $table);
280 }
281
282 // verifier que lire_config($cfg)==$store ?
283 return true;
284 }
285
286
287 /**
288 * metapack est inclue dans ecrire_config, mais on traite le cas ou il est explicite
289 * metapack:: dans le $cfg de ecrire_config.
290 * On renvoie simplement sur ecrire_config
291 *
292 * @param string $cfg
293 * @param mixed $store
294 * @return bool
295 */
296 function ecrire_config_metapack_dist($cfg, $store) {
297 // cas particulier en metapack::
298 // si on ecrit une chaine deja serializee, il faut la reserializer pour la rendre
299 // intacte en sortie ...
300 if (is_string($store) and strpos($store, ':') and unserialize($store)) {
301 $store = serialize($store);
302 }
303
304 return ecrire_config($cfg, $store);
305 }
306
307 /**
308 * Effacer une configuration : revient a ecrire une valeur null
309 *
310 * @param string $cfg
311 * @return bool
312 */
313 function effacer_config($cfg) {
314 ecrire_config($cfg, null);
315
316 return true;
317 }
318
319 /**
320 * Lister toutes les pages de configuration du site.
321 *
322 * Y compris celles fournies par des plugins.
323 *
324 * @uses _EXTENSION_SQUELETTES
325 * @uses find_all_in_path()
326 * @uses find_in_theme()
327 * @uses lister_formulaires_configurer()
328 *
329 * @param array $exclure
330 * @return array
331 */
332 function lister_configurer($exclure = array()) {
333 return array();
334
335 // lister les pages de config deja dans les menus
336 $deja = array();
337 foreach ($exclure as $id => $b) {
338 $url = ($b['url'] ? $b['url'] : $id);
339 if (!$b['url'] or !isset($exclure[$url])) {
340 if (strncmp($url, 'configurer_', 11) == 0) {
341 $deja[$url] = $b;
342 } elseif ($b['url'] == 'configurer' and preg_match(',cfg=([a-z0-9_]+),i', $b['args'], $match)) {
343 $deja["configurer_" . $match[1]] = $b;
344 }
345 }
346
347 }
348 $exclure = $exclure + $deja;
349
350 $icone_defaut = "images/configuration-16.png";
351 $liste = array();
352 $skels = array();
353 $forms = array();
354
355 // trouver toutes les pages configurer_xxx de l'espace prive
356 // et construire un tableau des entrees qui ne sont pas dans $deja
357 $pages = find_all_in_path("prive/squelettes/contenu/", "configurer_.*[.]" . _EXTENSION_SQUELETTES . '$');
358
359 foreach ($pages as $page) {
360 $configurer = basename($page, "." . _EXTENSION_SQUELETTES);
361 if (!isset($exclure[$configurer])) {
362 $liste[$configurer] = array(
363 'parent' => 'bando_configuration',
364 'url' => $configurer,
365 'titre' => _T("configurer:{$configurer}_titre"),
366 'icone' => find_in_theme($i = "images/{$configurer}-16.png") ? $i : $icone_defaut,
367 );
368 }
369 $skels[$configurer] = $page;
370 }
371
372 // analyser la liste des $skels pour voir les #FORMULAIRE_CONFIGURER_ inclus
373 foreach ($skels as $file) {
374 $forms = array_merge($forms, lister_formulaires_configurer($file));
375 }
376 $forms = array_flip($forms);
377
378 // trouver tous les formulaires/configurer_
379 // et construire un tableau des entrees
380 $pages = find_all_in_path("formulaires/", "configurer_.*[.]" . _EXTENSION_SQUELETTES . '$');
381 foreach ($pages as $page) {
382 $configurer = basename($page, "." . _EXTENSION_SQUELETTES);
383 if (!isset($forms[$configurer])
384 and !isset($liste[$configurer])
385 and !isset($exclure[$configurer])
386 ) {
387 $liste[$configurer] = array(
388 'parent' => 'bando_configuration',
389 'url' => 'configurer',
390 'args' => 'cfg=' . substr($configurer, 11),
391 'titre' => _T("configurer:{$configurer}_titre"),
392 'icone' => find_in_theme($i = "images/{$configurer}-16.png") ? $i : $icone_defaut,
393 );
394 }
395 }
396
397 return $liste;
398 }
399
400
401 /**
402 * Retourne la liste des formulaires de configuration
403 * présents dans le fichier dont l'adresse est donnée
404 *
405 * @param string $file
406 * Adresse du fichier
407 * @return array
408 * Liste des formulaires trouvés
409 **/
410 function lister_formulaires_configurer($file) {
411 $forms = array();
412
413 lire_fichier($file, $skel);
414 if (preg_match_all(",#FORMULAIRE_(CONFIGURER_[A-Z0-9_]*),", $skel, $matches, PREG_SET_ORDER)) {
415 $matches = array_map('end', $matches);
416 $matches = array_map('strtolower', $matches);
417 $forms = array_merge($forms, $matches);
418 }
419
420 // evaluer le fond en lui passant un exec coherent pour que les pipelines le reconnaissent
421 // et reperer les formulaires CVT configurer_xx insereres par les plugins via pipeline
422 $config = basename(substr($file, 0, -strlen("." . _EXTENSION_SQUELETTES)));
423 spip_log('Calcul de ' . "prive/squelettes/contenu/$config");
424 $fond = recuperer_fond("prive/squelettes/contenu/$config", array("exec" => $config));
425
426 // passer dans le pipeline affiche_milieu pour que les plugins puissent ajouter leur formulaires...
427 // et donc que l'on puisse les referencer aussi !
428 $fond = pipeline('affiche_milieu', array('args' => array("exec" => $config), 'data' => $fond));
429
430 // recuperer les noms des formulaires presents.
431 if (is_array($inputs = extraire_balises($fond, "input"))) {
432 foreach ($inputs as $i) {
433 if (extraire_attribut($i, 'name') == 'formulaire_action') {
434 $forms[] = ($c = extraire_attribut($i, 'value'));
435 }
436 }
437 }
438
439 return $forms;
440 }
441
442
443 /**
444 * Définir les `meta` de configuration
445 *
446 * @pipeline_appel configurer_liste_metas
447 *
448 * @uses url_de_base()
449 * @uses _DEFAULT_CHARSET
450 * @uses _DIR_IMG
451 * @uses _DIR_RACINE
452 *
453 * @return array
454 * Couples nom de la `meta` => valeur par défaut
455 */
456 function liste_metas() {
457 return pipeline('configurer_liste_metas', array(
458 'nom_site' => _T('info_mon_site_spip'),
459 'slogan_site' => '',
460 'adresse_site' => preg_replace(",/$,", "", url_de_base()),
461 'descriptif_site' => '',
462 'activer_logos' => 'oui',
463 'activer_logos_survol' => 'non',
464 'articles_surtitre' => 'non',
465 'articles_soustitre' => 'non',
466 'articles_descriptif' => 'non',
467 'articles_chapeau' => 'non',
468 'articles_texte' => 'oui',
469 'articles_ps' => 'non',
470 'articles_redac' => 'non',
471 'post_dates' => 'non',
472 'articles_urlref' => 'non',
473 'articles_redirection' => 'non',
474 'creer_preview' => 'non',
475 'taille_preview' => 150,
476 'articles_modif' => 'non',
477
478 'rubriques_descriptif' => 'non',
479 'rubriques_texte' => 'oui',
480
481 'accepter_inscriptions' => 'non',
482 'accepter_visiteurs' => 'non',
483 'prevenir_auteurs' => 'non',
484 'suivi_edito' => 'non',
485 'adresse_suivi' => '',
486 'adresse_suivi_inscription' => '',
487 'adresse_neuf' => '',
488 'jours_neuf' => '',
489 'quoi_de_neuf' => 'non',
490 'preview' => ',0minirezo,1comite,',
491
492 'syndication_integrale' => 'oui',
493 'charset' => _DEFAULT_CHARSET,
494 'dir_img' => substr(_DIR_IMG, strlen(_DIR_RACINE)),
495
496 'multi_rubriques' => 'non',
497 'multi_secteurs' => 'non',
498 'gerer_trad' => 'non',
499 'langues_multilingue' => '',
500
501 'version_html_max' => 'html4',
502
503 'type_urls' => 'page',
504
505 'email_envoi' => '',
506 'email_webmaster' => '',
507 'auto_compress_http' => 'non',
508 ));
509 }
510
511 /**
512 * Mets les `meta` à des valeurs conventionnelles quand elles sont vides
513 * et recalcule les langues
514 *
515 * @param array $liste_meta
516 * @return void
517 */
518 function actualise_metas($liste_meta) {
519 $meta_serveur =
520 array(
521 'version_installee',
522 'adresse_site',
523 'alea_ephemere_ancien',
524 'alea_ephemere',
525 'alea_ephemere_date',
526 'langue_site',
527 'langues_proposees',
528 'date_calcul_rubriques',
529 'derniere_modif',
530 'optimiser_table',
531 'drapeau_edition',
532 'creer_preview',
533 'taille_preview',
534 'creer_htpasswd',
535 'creer_htaccess',
536 'gd_formats_read',
537 'gd_formats',
538 'netpbm_formats',
539 'formats_graphiques',
540 'image_process',
541 'plugin_header',
542 'plugin'
543 );
544 // verifier le impt=non
545 sql_updateq('spip_meta', array('impt' => 'non'), sql_in('nom', $meta_serveur));
546
547 while (list($nom, $valeur) = each($liste_meta)) {
548 if (!isset($GLOBALS['meta'][$nom]) or !$GLOBALS['meta'][$nom]) {
549 ecrire_meta($nom, $valeur);
550 }
551 }
552
553 include_spip('inc/rubriques');
554 $langues = calculer_langues_utilisees();
555 ecrire_meta('langues_utilisees', $langues);
556 }
557
558
559 //
560 // Gestion des modifs
561 //
562
563 /**
564 * Appliquer les modifications apportées aux `metas`
565 *
566 * Si `$purger_skel` est à `true`, on purge le répertoire de cache des squelettes
567 *
568 * @uses liste_metas()
569 * @uses ecrire_meta()
570 * @uses purger_repertoire()
571 *
572 * @param bool $purger_skel
573 * @return void
574 */
575 function appliquer_modifs_config($purger_skel = false) {
576
577 foreach (liste_metas() as $i => $v) {
578 if (($x = _request($i)) !== null) {
579 ecrire_meta($i, $x);
580 } elseif (!isset($GLOBALS['meta'][$i])) {
581 ecrire_meta($i, $v);
582 }
583 }
584
585 if ($purger_skel) {
586 include_spip('inc/invalideur');
587 purger_repertoire(_DIR_SKELS);
588 }
589 }
590
591 /**
592 * Mettre à jour l'adresse du site à partir d'une valeur saisie
593 * (ou auto détection si vide)
594 *
595 * @param string $adresse_site
596 * @return string
597 */
598 function appliquer_adresse_site($adresse_site) {
599 if ($adresse_site !== null) {
600 if (!strlen($adresse_site)) {
601 $GLOBALS['profondeur_url'] = _DIR_RESTREINT ? 0 : 1;
602 $adresse_site = url_de_base();
603 }
604 $adresse_site = preg_replace(",/?\s*$,", "", $adresse_site);
605
606 if (!tester_url_absolue($adresse_site)) {
607 $adresse_site = "http://$adresse_site";
608 }
609
610 ecrire_meta('adresse_site', $adresse_site);
611 }
612
613 return $adresse_site;
614 }