source: spip-zone/_plugins_/_stable_/cfg/inc/cfg_formulaire.php @ 19405

Last change on this file since 19405 was 19405, checked in by marcimat@…, 13 years ago
  • cosmetique : ne pas afficher une erreur sur la page ?exec=cfg qui n'a pas de fond renseigné
File size: 17.0 KB
Line 
1<?php
2
3
4/*
5 * Plugin CFG pour SPIP
6 * (c) toggg 2007, distribue sous licence GNU/GPL
7 * Documentation et contact: http://www.spip-contrib.net/
8 *
9 */
10
11if (!defined("_ECRIRE_INC_VERSION")) return;
12
13
14// la classe cfg represente une page de configuration
15class cfg_formulaire
16{
17// le storage, par defaut metapack: spip_meta serialise
18        var $storage = 'metapack';
19// l'objet de classe cfg_<storage> qui assure lecture/ecriture des config
20        var $sto = null;
21// les options de creation de cet objet
22        var $optsto = array();
23// le "faire" de autoriser($faire), par defaut, autoriser_configurer_dist()
24        var $autoriser = 'configurer';
25// la config est-elle permise ?
26        var $_permise = false;
27// en cas de refus, un message informatif [(#REM) refus=...]
28        var $refus = '';
29// partie du fond cfg a inserer dans le head par le pipeline header_prive (todo insert_head?)
30        var $head = '';
31// le nom du meta (ou autre) ou va etre stocke la config concernee
32        var $nom = '';
33// le fond html utilise , en general pour config simple idem $nom
34        var $vue = '';
35// pour une config multiple , l'id courant
36        var $cfg_id = '';
37// sous tableau optionel du meta ou va etre stocke le fragment de config
38// vide = a la "racine" du meta nomme $nom
39        var $casier = '';
40// descriptif
41        var $descriptif = '';
42// cfg doit-il encadrer le formulaire tout seul ?
43        var $presentation = 'auto';
44// cfg doit-il afficher un lien vers le fond sous forme d'onglet
45// dans la page ?exec=cfg
46        var $onglet = 'oui'; 
47// compte-rendu des mises a jour, vide == pas d'erreur
48        var $message = '';
49// afficher ce compte rendu ?
50        var $afficher_messages = true;
51// liens optionnels sur des sous-config <!-- liens*=xxx -->
52        var $liens = array();
53// liens optionnels sur des sous-config pour des fonds utilisant un champ multiple  <!-- liens_multi*=xxx -->
54        var $liens_multi = array();
55// les champs trouve dans le fond
56        var $champs = array();
57// les champs index
58        var $champs_id = array();
59// leurs valeurs
60        var $val = array();
61// nom de la table sql pour storage extra ou table
62        var $table = '';
63// autoriser l'insertion de nouveau contenu dans une table sans donner d'identifiant ?
64        var $autoriser_absence_id = 'non';
65// pour tracer les valeurs modifiees
66        var $log_modif = '';
67// stockage du fond compile par recuperer_fond()
68        var $fond_compile = '';
69// configuration des types
70//TODO traductions
71        var $types = array(
72                  'id' => array('#^[a-z_]\w*$#i', 'lettre ou &#095; suivie de lettres, chiffres ou &#095;'),
73                  'idnum' => array('#^\d+$#', 'chiffres', 'intval'),
74                  'pwd' => array('#^.{5}#', 'minimum 5 caract&egrave;res;'));
75       
76        /*
77         * Constructeur de la classe
78         */
79        function cfg_formulaire($nom, $cfg_id = '', $opt = array())
80        {
81                $this->nom = $this->vue = $nom;
82                $this->cfg_id = $cfg_id;
83                $this->base_url = generer_url_ecrire('');
84                foreach ($opt as $o=>$v) {
85                        $this->$o = $v;
86                }
87                       
88                // charger les donnees du fond demande
89                $this->charger();
90        }
91       
92        // pre-analyser le formulaire
93        // c'est a dire recuperer les parametres CFG
94        // et les noms des champs du formulaire
95        function charger(){
96                // lecture de la vue (fond cfg)
97                // il s'agit de recuperer le contenu du fichier
98                if ($this->vue) {
99                        $fichier = find_in_path($nom = 'fonds/cfg_' . $this->vue .'.html');
100                        if (!lire_fichier($fichier, $this->controldata)) {
101                                $this->message .=  _T('cfg:erreur_lecture', array('nom' => $nom));
102                        }
103                }
104               
105                // recherche et stockage des parametres de cfg
106                $this->recuperer_parametres();
107                       
108                // recherche et stockage des noms de champs de formulaire
109                $this->message .=  $this->recuperer_noms_champs();
110               
111                // est-ton autoriser a voir ce formulaire ?
112                $this->_permise = $this->autoriser();
113               
114                /*
115                 * Cas des champs multi, si des champs (Y)
116                 * sont declares id par la classe cfg_id,
117                 * <input type='x' name='Yn' class='cfg_id'>
118                 * on les ajoute dans le chemin pour retrouver les donnees
119                 * #CONFIG{.../y1/y2/y3/...}
120                 *
121                 */
122                if (_request('_cfg_affiche')) {
123                        $this->cfg_id = implode('/', array_map('_request', $this->champs_id));
124            } 
125               
126                // creer le storage et lire les valeurs
127                $this->storage = strtolower(trim($this->storage));
128                $classto = 'cfg_' . $this->storage;
129                include_spip('inc/' . $classto);
130                $this->sto = new $classto($this, $this->optsto);
131                $this->val = $this->sto->lire();
132                // stocker le fait que l'on a charge les valeurs
133                $this->charger = true;
134        }
135
136
137        /*
138         * Determine l'arborescence ou CFG doit chercher les valeurs deja enregistrees
139         * si nom=toto, casier=chose/truc, cfg_id=2,
140         * cfg cherchera dans #CONFIG{toto/chose/truc/2}
141         *
142         */
143        function nom_config()
144        {
145            return $this->nom . ($this->casier ? '/' . $this->casier : '') .
146                        ($this->cfg_id ? '/' . $this->cfg_id : '');
147        }
148
149
150
151        /*
152         * Recherche et stockage
153         * des parametres #REM passes a CFG
154         * (DEPRECIE)
155         */
156        function recuperer_parametres_rem(){
157                // cas de #REM (deprecie)
158                preg_replace_callback('/(\[\(#REM\) ([a-z0-9_]\w+)(\*)?=)(.*?)\]/sim',
159                                        array(&$this, 'post_params'), $this->controldata);
160        }
161       
162       
163        // cette fonction recherche et stocke les parametres passes a cfg par <!-- param=valeur -->
164        // ces lignes sont alors effacees du code html. Ces proprietes sont lues apres recuperer_fond(),
165        // et interpretent donc les balises spip et les chaines de langues
166        //
167        // si la fonction est appelee 2 fois, les parametres identiques ne seront pas copies
168        // sauf si le parametre est un tableau (<!-- param*=valeur -->), les valeurs seront dupliquees
169        function recuperer_parametres(){
170               
171                // pour compatibilite, recuperer l'ancien code #REM
172                $this->recuperer_parametres_rem();
173               
174                $this->rempar = array(array());
175                if (preg_match_all('/<!-- [a-z0-9_]\w+\*?=/i', $this->controldata, $this->rempar)) {
176                        // il existe des champs <!-- param=valeur -->, on les stocke
177                        $this->recuperer_fond();
178                        $this->current_rempar = 0;
179                        $this->fond_compile = preg_replace_callback('/(<!-- ([a-z0-9_]\w+)(\*)?=)(.*?)-->/sim',
180                                                                array(&$this, 'post_params'), $this->fond_compile);
181                        // s'il en reste : il y a un probleme !
182                        if (preg_match('/<!-- [a-z0-9_]\w+\*?=/', $this->fond_compile)) {
183                                die('erreur manque parametre externe: '
184                                        . htmlentities(var_export($this->rempar, true)));
185                        }
186                }               
187        }
188       
189        // une fonction pour effacer les parametres du code html
190        // ce qui evite de dupliquer les tableaux
191        // (si on utilisait recuperer_parametres() a la place)
192        function effacer_parametres(){
193                        $this->fond_compile = preg_replace('/(<!-- ([a-z0-9_]\w+)(\*)?=)(.*?)-->/sim',
194                                                                '', $this->fond_compile);               
195        }
196       
197        /*
198         *
199         * Recherche et stockage
200         * des noms des champs (y) du formulaire
201         * <input type="x" name="y"... />
202         *
203         */     
204        function recuperer_noms_champs(){       
205                if (!$this->vue) return;
206               
207                // recherche d'au moins un champ de formulaire pour savoir si la vue est valide
208                $this->recuperer_fond();
209                if (!preg_match_all(
210                  '#<(?:(select|textarea)|input type="(text|password|checkbox|radio|hidden)") name="(\w+)(\[\])?"(?: class="[^"]*?(?:type_(\w+))?[^"]*?(?:cfg_(\w+))?[^"]*?")?( multiple=)?[^>]*?>#ims',
211                                                $this->fond_compile, $matches, PREG_SET_ORDER)) {
212                        return _T('cfg:pas_de_champs_dans', array('nom' => $this->vue));
213                }
214               
215                // stockage des champs trouves dans $this->champs
216                foreach ($matches as $regs) {
217                        if (substr($regs[3], 0, 5) == '_cfg_') {
218                                continue;
219                        }
220                    if (!empty($regs[1])) {
221                        $regs[2] = strtolower($regs[1]);
222                            if ($regs[2] == 'select' && !empty($regs[7])) {
223                                $regs[2] = 'selmul';
224                            }
225                    }
226                    $this->champs[$regs[3]] =
227                        array('inp' => $regs[2], 'typ' => '', 'array' => !empty($regs[4]));
228                    if (!empty($regs[5])) {
229                        $this->champs[$regs[3]]['typ'] = $regs[5];
230                    }
231                    if (!empty($regs[6])) {
232                        $this->champs[$regs[3]]['cfg'] = $regs[6];
233                        if ($regs[6] == 'id') {
234                                $this->champs[$regs[3]]['id'] = count($this->champs_id);
235                                $this->champs_id[] = $regs[3];
236                        }
237                    }
238            }
239            return '';
240        }       
241         
242       
243        /*
244         *
245         * Compiler le fond CFG si ce n'est pas fait
246         *
247         */
248        function recuperer_fond($contexte = array(), $forcer = false){
249                if (!$this->fond_compile OR $forcer){
250                        include_spip('inc/presentation'); // offrir les fonctions d'espace prive
251                        include_spip('public/assembler');
252                        // rendre editable systematiquement
253                        // sinon, ceux qui utilisent les fonds CFG avec l'API des formulaires dynamiques
254                        // et mettent des [(#ENV**{editable}|?{' '}) ... ] ne verraient pas leurs variables
255                        // dans l'environnement vu que CFG ne pourrait pas lire les champs du formulaire
256                        $contexte['editable'] = "oui";
257                        $this->fond_compile = recuperer_fond(
258                                        'fonds/cfg_' . $this->vue,
259                                        $this->val 
260                                                ? array_merge($contexte, $this->val) 
261                                                : $contexte);
262                }
263        }
264       
265       
266        /*
267         * Verifie les autorisations
268         * d'affichage du formulaire
269         * (parametre autoriser=quoi)
270         */
271        function autoriser()
272        {
273                include_spip('inc/autoriser');
274                return autoriser($this->autoriser);
275        }
276
277        /*
278         * Log le message passe en parametre
279         * $this->log('message');
280         */
281        function log($message)
282        {
283                ($GLOBALS['auteur_session'] && ($qui = $GLOBALS['auteur_session']['login']))
284                || ($qui = $GLOBALS['ip']);
285                spip_log('cfg (' . $this->nom_config() . ') par ' . $qui . ': ' . $message);
286        }
287
288       
289        /*
290         * Modifie ou supprime les donnees postees par le formulaire
291         */
292        function modifier($supprimer = false)
293        {
294                // suppression ?
295                if ($supprimer) {
296                        $ok = $this->sto->modifier($supprimer);
297                        // dans le cas d'une suppression, il faut vider $this->val qui
298                        // contient encore les valeurs du formulaire, sinon elles sont
299                        // passees dans le fond et le formulaire garde les informations
300                        // d'avant la suppression
301                        if ($ok) {
302                                $this->val = array();
303                                $msg = _T('cfg:config_supprimee', array('nom' => $this->nom_config()));
304                        } else {
305                                $msg = _T('cfg:erreur_suppression', array('nom' => $this->nom_config()));
306                        }
307                        $this->message .= $msg;
308                        $this->log($msg);
309                }
310               
311                // sinon verifier que le controle
312                // n'a pas retourne de message d'erreur
313                //
314                // /!\ cela implique d'avoir lance $this->verifier()
315                // a un moment donne (#FORMULAIRE_CFG le teste dans valider.php)
316                // $this->message sera vide systematiquement si verifier() n'est pas execute
317                else if ($this->message) {
318                }
319               
320                // si elles ont changees, on modifie !
321                else {
322                        $ok = $this->sto->modifier();
323                        $this->message .= ($msg = $ok 
324                                                ? _T('cfg:config_enregistree', array('nom' => $this->nom_config())) 
325                                                : _T('cfg:erreur_enregistrement', array('nom' => $this->nom_config())));
326                        $this->log($msg . ' ' . $this->log_modif);
327                }
328
329                // pipeline 'cfg_post_edition'
330                $this->message = pipeline('cfg_post_edition',array('args'=>array('nom_config'=>$this->nom_config()),'data'=>$this->message));
331        }
332
333
334        /*
335         * Enregistre les changements proposes
336         * si l'on est bien authentifie (action)
337         */
338        function enregistrer(){
339
340                // enregistrement ou suppression ?
341                $enregistrer = $supprimer = false;
342                if  (!_request('_cfg_ok') &&  !_request('_cfg_delete')) 
343                        return false;
344       
345                if  ((!$supprimer = _request('_cfg_delete')) && $this->message)
346                        return false;
347       
348                $securiser_action = charger_fonction('securiser_action', 'inc');
349                $securiser_action();
350                       
351                // suppression
352                if ($supprimer) {
353                        $this->modifier('supprimer');
354               
355                // sinon modification
356                // seulement si les types de valeurs attendus sont corrects
357                } elseif (!$this->message) {
358                       
359                        // lorsque c'est un champ de type multi que l'on modifie
360                        // et si l'identifiant a change, il faut soit le copier, soit de deplacer
361                        $this->new_id = implode('/', array_map('_request', $this->champs_id));
362                        if ($this->new_id != $this->cfg_id && !_request('_cfg_copier')) {
363                                $this->modifier('supprimer');
364                        }
365                        $this->cfg_id = $this->new_id;
366                       
367                       
368                        $this->modifier();
369                }
370               
371                return true;           
372        }
373       
374       
375       
376        /*
377         * Gere le traitement du formulaire.
378         *
379         * Si le chargement ou le controle n'ont pas ete fait,
380         * la fonction s'en occupe.
381         *
382         */
383        function traiter()
384        {
385                if (!$this->charger) $this->charger();
386                if (!$this->controler) $this->verifier();
387               
388                // est on autorise ?
389                if (!$this->_permise) return;
390
391                // enregistrer les modifs
392                if (!$this->enregistrer())
393                        return;
394                       
395                // Si le fond du formulaire demande expressement une redirection
396                // par <!-- rediriger=1 -->, on stocke le message dans une meta
397                // et on redirige le client, de maniere a charger la page
398                // avec la nouvelle config (ce qui permet par exemple a Autorite
399                // de controler d'eventuels conflits generes par les nouvelles autorisations)
400                if ($this->rediriger && $this->message) {
401                        include_spip('inc/meta');
402                        ecrire_meta('cfg_message_'.$GLOBALS['auteur_session']['id_auteur'], $this->message, 'non');
403                        if (defined('_COMPAT_CFG_192')) ecrire_metas();
404                        include_spip('inc/headers');
405                        redirige_par_entete(parametre_url(self(),null,null,'&'));
406                }
407        }
408
409        /*
410         * Doit controler la validite des valeurs transmises
411         * (le stockage de ces valeurs devrait etre ailleurs qu'ici)
412         *
413         * Verifie les valeurs postees.
414         * - stocke les valeurs qui ont changees dans $this->val[$nom_champ] = 'nouvelle_valeur'
415         * - verifie que les types de valeurs attendus sont corrects ($this->types)
416         *
417         * retourne les messages d'erreur
418         */
419        function verifier() {
420            $erreurs = array();
421           
422                if (!$this->charger) $this->charger();
423               
424                // si on a pas poste de formulaire, pas la peine de controler
425                // ce qui mettrait de fausses valeurs dans l'environnement
426                if  (!_request('_cfg_ok') && !_request('_cfg_delete')) return $erreurs;
427               
428                // stoockage des nouvelles valeurs
429                foreach ($this->champs as $name => $def) {
430                        // enregistrement des valeurs postees
431                        $oldval = $this->val[$name];
432                    $this->val[$name] = _request($name);
433                   
434                    // tracer les modifications
435                    if ($oldval != $this->val[$name]) {
436                        $this->log_modif .= $name . ':' . var_export($oldval, true) . '/' . var_export($this->val[$name], true) .', ';
437                    }
438                   
439                    // tester la validite des champs
440                    // (TODO: scinder $erreurs et $this->message)
441                    if ($erreur = $this->controler_champ($name)) {
442                        $this->message .= $erreur."<br />\n";
443                        $erreurs[$name] = $erreur;
444                    }
445            }
446               
447                // si pas de changement, pas la peine de continuer
448                if (!$this->log_modif) {
449                        $this->message .= _T('cfg:pas_de_changement', array('nom' => $this->nom_config()));
450                        $erreurs['message_erreur'] = _T('cfg:pas_de_changement', array('nom' => $this->nom_config()));
451                }
452
453                // stocker le fait que l'on a controle les valeurs
454                $this->controler = true;
455                       
456            return $erreurs;
457        }
458       
459       
460        // verification du type de valeur attendue
461        // cela est defini par un nom de class css (class="type_idnum")
462        // 'idnum' etant defini dans $this->types['idnum']...
463        // si le nom du champ possede une traduction, il sera traduit.
464        //
465        // API a revoir, les controles sont trop sommaire,
466        // il faut pouvoir tester une plage de valeur par exemple, simplement
467        // une preg n'est pas ideale
468        // De plus, le multilinguisme n'est pas fait.
469        function controler_champ($name){
470                $type = $this->champs[$name]['typ'];
471                if (!empty($type) && isset($this->types[$type])) {
472                        $dtype = $this->types[$type];
473                        if (!preg_match($dtype[0], $this->val[$name])) {
474                                // erreur
475                                return $name . '&nbsp;:' . $dtype[1];
476                        }
477                }
478                // pas d'erreur ou pas de test
479                return;
480        }
481
482
483
484        /*
485         * Fabriquer les balises des champs d'apres un modele fonds/cfg_<driver>.html
486         * $contexte est un tableau (nom=>valeur)
487         * qui sera enrichi puis passe a recuperer_fond
488         */
489        function formulaire($contexte = array())
490        {
491                if (!find_in_path('fonds/cfg_' . $this->vue . '.html'))
492                        return '';
493
494                $contexte['_cfg_'] = $this->creer_hash_cfg();
495
496                // recuperer le fond avec le contexte
497                // forcer le calcul.
498                $this->recuperer_fond($contexte, true);
499                $this->effacer_parametres(); // pour enlever les <!-- param=valeur --> ... sans dedoubler le contenu lorsque ce sont des tableau (param*=valeur)
500                return $this->fond_compile;
501        }
502       
503       
504        function creer_hash_cfg(){
505                include_spip('inc/securiser_action');
506            $arg = 'cfg0.0.0-' . $this->nom . '-' . $this->vue;
507                return 
508                        '?exec=cfg&cfg=' . $this->nom .
509                        '?cfg=' . $this->nom .
510                        '&cfg_vue=' . $this->vue .
511                        '&cfg_id=' . $this->cfg_id .
512                        '&base_url=' . $this->base_url .
513                    '&lang=' . $GLOBALS['spip_lang'] .
514                    '&arg=' . $arg .
515                    '&hash=' .  calculer_action_auteur('-' . $arg);             
516        }
517       
518        /*
519         * callback pour interpreter les parametres objets du formulaire
520         * commun avec celui de set_vue()
521         *
522         * Parametres :
523         * - $regs[2] = 'param'
524         * - $regs[3] = '*' ou ''
525         * - $regs[4] = 'valeur'
526         *
527         * Lorsque des parametres sont passes dans le formulaire
528         * par <!-- param=valeur -->
529         * stocker $this->param=valeur
530         *
531         * Si <!-- param*=valeur -->
532         * Stocker $this->param[]=valeur
533         *
534         */
535        function post_params($regs) {
536                // a priori, eviter l'injection du motif
537                if (isset($this->rempar)) {
538                        if (!isset($this->rempar[0][$this->current_rempar])
539                                || $regs[1] != $this->rempar[0][$this->current_rempar++]) {
540                                die("erreur parametre interne: " . htmlentities(var_export($regs[1], true)));
541                        }
542                }
543                // $regs[3] peut valoir '*' pour signaler un tableau
544                $regs[4] = trim($regs[4]);
545               
546                if (empty($regs[3])) {
547                    $this->{$regs[2]} = $regs[4];
548                } elseif (is_array($this->{$regs[2]})) {
549                    $this->{$regs[2]}[] = $regs[4];
550                }
551                // plus besoin de garder ca
552                return '';
553        }
554}
555
556
557function cfg_get_formulaire($cfg, $cfg_id=""){
558        return new cfg_formulaire($cfg, $cfg_id);
559}
560?>
Note: See TracBrowser for help on using the repository browser.