source: spip-zone/_plugins_/saisies/trunk/inc/saisies_afficher.php @ 112938

Last change on this file since 112938 was 112938, checked in by maieul@…, 2 years ago

toujours sur la securité du test dans afficher_si : empecher d'executer des fonctions sql en une seul ligne

File size: 22.8 KB
Line 
1<?php
2
3/**
4 * Gestion de l'affichage des saisies.
5 *
6 * @return SPIP\Saisies\Afficher
7 **/
8
9// Sécurité
10if (!defined('_ECRIRE_INC_VERSION')) {
11        return;
12}
13
14/**
15 * Indique si une saisie peut être affichée.
16 *
17 * On s'appuie sur l'éventuelle clé "editable" du $champ.
18 * Si editable vaut :
19 *    - absent : le champ est éditable
20 *    - 1, le champ est éditable
21 *    - 0, le champ n'est pas éditable
22 *    - -1, le champ est éditable s'il y a du contenu dans le champ (l'environnement)
23 *         ou dans un de ses enfants (fieldsets)
24 *
25 * @param array $champ
26 *                                 Tableau de description de la saisie
27 * @param array $env
28 *                                 Environnement transmis à la saisie, certainement l'environnement du formulaire
29 * @param bool  $utiliser_editable
30 *                                 - false pour juste tester le cas -1
31 *
32 * @return bool
33 *              Retourne un booléen indiquant l'état éditable ou pas :
34 *              - true si la saisie est éditable (peut être affichée)
35 *              - false sinon
36 */
37function saisie_editable($champ, $env, $utiliser_editable = true) {
38        if ($utiliser_editable) {
39                // si le champ n'est pas éditable, on sort.
40                if (!isset($champ['editable'])) {
41                        return true;
42                }
43                $editable = $champ['editable'];
44
45                if ($editable > 0) {
46                        return true;
47                }
48                if ($editable == 0) {
49                        return false;
50                }
51        }
52
53        // cas -1
54        // name de la saisie
55        if (isset($champ['options']['nom'])) {
56                // si on a le name dans l'environnement, on le teste
57                $nom = $champ['options']['nom'];
58                if (isset($env[$nom])) {
59                        return $env[$nom] ? true : false;
60                }
61        }
62        // sinon, si on a des sous saisies
63        if (isset($champ['saisies']) and is_array($champ['saisies'])) {
64                foreach ($champ['saisies'] as $saisie) {
65                        if (saisie_editable($saisie, $env, false)) {
66                                return true;
67                        }
68                }
69        }
70
71        // aucun des paramètres demandés n'avait de contenu
72        return false;
73}
74
75/**
76 * Génère une saisie à partir d'un tableau la décrivant et de l'environnement.
77 *
78 * @param array $champ
79 *                     Description de la saisie.
80 *                     Le tableau doit être de la forme suivante :
81 *                     array(
82 *                     'saisie' => 'input',
83 *                     'options' => array(
84 *                     'nom' => 'le_name',
85 *                     'label' => 'Un titre plus joli',
86 *                     'obligatoire' => 'oui',
87 *                     'explication' => 'Remplissez ce champ en utilisant votre clavier.'
88 *                     )
89 *                     )
90 * @param array $env
91 *                     Environnement du formulaire
92 *                     Permet de savoir les valeurs actuelles des contenus des saisies,
93 *                     les erreurs eventuelles présentes...
94 *
95 * @return string
96 *                Code HTML des saisies de formulaire
97 */
98function saisies_generer_html($champ, $env = array()) {
99        // Si le parametre n'est pas bon, on genere du vide
100        if (!is_array($champ)) {
101                return '';
102        }
103
104        // Si la saisie n'est pas editable, on sort aussi.
105        if (!saisie_editable($champ, $env)) {
106                return '';
107        }
108
109        $contexte = array();
110
111        // On sélectionne le type de saisie
112        $contexte['type_saisie'] = $champ['saisie'];
113        // Identifiant unique de saisie, si present
114        if (isset($champ['identifiant'])) {
115                $contexte['id_saisie'] = $champ['identifiant'];
116        }
117
118        // Peut-être des transformations à faire sur les options textuelles
119        $options = isset($champ['options']) ? $champ['options'] : array();
120        foreach ($options as $option => $valeur) {
121                if ($option == 'datas') {
122                        // exploser une chaine datas en tableau (applique _T_ou_typo sur chaque valeur)
123                        $options[$option] = saisies_chaine2tableau($valeur);
124                } else {
125                        $options[$option] = _T_ou_typo($valeur, 'multi');
126                }
127        }
128
129        // compatibilité li_class > conteneur_class
130        if (!empty($options['li_class'])) {
131                $options['conteneur_class'] = $options['li_class'];
132        }
133
134        // On ajoute les options propres à la saisie
135        $contexte = array_merge($contexte, $options);
136
137        // On ajoute aussi les infos de vérification, si cela peut se faire directement en HTML5
138        if (isset($champ['verifier'])) {
139                $contexte = array_merge($contexte, array('verifier'=>$champ['verifier']));
140        }
141
142        // Si env est définie dans les options ou qu'il y a des enfants, on ajoute tout l'environnement
143        if (isset($contexte['env']) or (isset($champ['saisies']) and is_array($champ['saisies']))) {
144                unset($contexte['env']);
145
146                // on sauve l'ancien environnement
147                // car les sous-saisies ne doivent pas être affectees
148                // par les modification sur l'environnement servant à generer la saisie mère
149                $contexte['_env'] = $env;
150
151                // À partir du moment où on passe tout l'environnement,
152                // il faut enlever certains éléments qui ne doivent absolument provenir que des options
153                unset($env['inserer_debut']);
154                unset($env['inserer_fin']);
155                $saisies_disponibles = saisies_lister_disponibles();
156                if (isset($saisies_disponibles[$contexte['type_saisie']])
157                        and isset($saisies_disponibles[$contexte['type_saisie']]['options'])
158                        and is_array($saisies_disponibles[$contexte['type_saisie']]['options'])) {
159                        $options_a_supprimer = saisies_lister_champs($saisies_disponibles[$contexte['type_saisie']]['options']);
160                        foreach ($options_a_supprimer as $option_a_supprimer) {
161                                unset($env[$option_a_supprimer]);
162                        }
163                }
164
165                $contexte = array_merge($env, $contexte);
166        } else {
167                // Sinon on ne sélectionne que quelques éléments importants
168                // On récupère la liste des erreurs
169                $contexte['erreurs'] = $env['erreurs'];
170                // On récupère la langue de l'objet si existante
171                if (isset($env['langue'])) {
172                        $contexte['langue'] = $env['langue'];
173                }
174                // On ajoute toujours le bon self
175                $contexte['self'] = self();
176        }
177
178        // Dans tous les cas on récupère de l'environnement la valeur actuelle du champ
179        // Si le nom du champ est un tableau indexé, il faut parser !
180        if (
181                isset($contexte['nom'])
182                and preg_match('/([\w]+)((\[[\w]+\])+)/', $contexte['nom'], $separe)
183                and isset($env[$separe[1]])
184        ) {
185                $contexte['valeur'] = $env[$separe[1]];
186                preg_match_all('/\[([\w]+)\]/', $separe[2], $index);
187                // On va chercher au fond du tableau
188                foreach ($index[1] as $cle) {
189                        $contexte['valeur'] = isset($contexte['valeur'][$cle]) ? $contexte['valeur'][$cle] : null;
190                }
191        } elseif (isset($contexte['nom']) and isset($env[$contexte['nom']])) {
192                // Sinon la valeur est juste celle du nom si elle existe
193                $contexte['valeur'] = $env[$contexte['nom']];
194        } else {
195                // Sinon rien
196                $contexte['valeur'] = null;
197        }
198
199        // Si ya des enfants on les remonte dans le contexte
200        if (isset($champ['saisies']) and is_array($champ['saisies'])) {
201                $contexte['saisies'] = $champ['saisies'];
202        }
203
204        // On génère la saisie
205        return recuperer_fond(
206                'saisies/_base',
207                $contexte
208        );
209}
210
211/**
212 * Génère une vue d'une saisie à partir d'un tableau la décrivant.
213 *
214 * @see saisies_generer_html()
215 *
216 * @param array $saisie
217 *                               Tableau de description d'une saisie
218 * @param array $env
219 *                               L'environnement, contenant normalement la réponse à la saisie
220 * @param array $env_obligatoire
221 *                               ???
222 *
223 * @return string
224 *                Code HTML de la vue de la saisie
225 */
226function saisies_generer_vue($saisie, $env = array(), $env_obligatoire = array()) {
227        // Si le paramètre n'est pas bon, on génère du vide
228        if (!is_array($saisie)) {
229                return '';
230        }
231
232        $contexte = array();
233
234        // On sélectionne le type de saisie
235        $contexte['type_saisie'] = $saisie['saisie'];
236
237        // Peut-être des transformations à faire sur les options textuelles
238        $options = $saisie['options'];
239        foreach ($options as $option => $valeur) {
240                if ($option == 'datas') {
241                        // exploser une chaine datas en tableau (applique _T_ou_typo sur chaque valeur)
242                        $options[$option] = saisies_chaine2tableau($valeur);
243                } else {
244                        $options[$option] = _T_ou_typo($valeur, 'multi');
245                }
246        }
247
248        // On ajoute les options propres à la saisie
249        $contexte = array_merge($contexte, $options);
250
251        // Si env est définie dans les options ou qu'il y a des enfants, on ajoute tout l'environnement
252        if (isset($contexte['env']) or (isset($saisie['saisies']) and is_array($saisie['saisies']))) {
253                unset($contexte['env']);
254
255                // on sauve l'ancien environnement
256                // car les sous-saisies ne doivent pas être affectees
257                // par les modification sur l'environnement servant à generer la saisie mère
258                $contexte['_env'] = $env;
259
260                // À partir du moment où on passe tout l'environnement, il faut enlever
261                // certains éléments qui ne doivent absolument provenir que des options
262                $saisies_disponibles = saisies_lister_disponibles();
263
264                if (isset($saisies_disponibles[$contexte['type_saisie']]['options'])
265                        and is_array($saisies_disponibles[$contexte['type_saisie']]['options'])) {
266                        $options_a_supprimer = saisies_lister_champs($saisies_disponibles[$contexte['type_saisie']]['options']);
267                        foreach ($options_a_supprimer as $option_a_supprimer) {
268                                unset($env[$option_a_supprimer]);
269                        }
270                }
271
272                $contexte = array_merge($env, $contexte);
273        }
274
275        // Dans tous les cas on récupère de l'environnement la valeur actuelle du champ
276
277        // On regarde en priorité s'il y a un tableau listant toutes les valeurs
278        if (!empty($env['valeurs']) and is_array($env['valeurs']) and isset($env['valeurs'][$contexte['nom']])) {
279                $contexte['valeur'] = $env['valeurs'][$contexte['nom']];
280        } elseif (preg_match('/([\w]+)((\[[\w]+\])+)/', $contexte['nom'], $separe)) {
281                // Si le nom du champ est un tableau indexé, il faut parser !
282                $contexte['valeur'] = $env[$separe[1]];
283                preg_match_all('/\[([\w]+)\]/', $separe[2], $index);
284                // On va chercher au fond du tableau
285                foreach ($index[1] as $cle) {
286                        $contexte['valeur'] = $contexte['valeur'][$cle];
287                }
288        } else {
289                // Sinon la valeur est juste celle du nom
290                // certains n'ont pas de nom (fieldset)
291                $contexte['valeur'] = isset($env[$contexte['nom']]) ? $env[$contexte['nom']] : '';
292        }
293
294        // Si ya des enfants on les remonte dans le contexte
295        if (isset($saisie['saisies']) and is_array($saisie['saisies'])) {
296                $contexte['saisies'] = $saisie['saisies'];
297        }
298
299        if (is_array($env_obligatoire)) {
300                $contexte = array_merge($contexte, $env_obligatoire);
301        }
302
303        // On génère la saisie
304        return recuperer_fond(
305                'saisies-vues/_base',
306                $contexte
307        );
308}
309
310/**
311 * Génère, à partir d'un tableau de saisie le code javascript ajouté à la fin de #GENERER_SAISIES
312 * pour produire un affichage conditionnel des saisies ayant une option afficher_si
313 *
314 * @param array  $saisies
315 *                        Tableau de descriptions des saisies
316 * @param string $id_form
317 *                        Identifiant unique pour le formulaire
318 *
319 * @return text
320 *              Code javascript
321 */
322function saisies_generer_js_afficher_si($saisies, $id_form) {
323        $i = 0;
324        $saisies = saisies_lister_par_nom($saisies, true);
325        $code = '';
326        $code .= "$(function(){\n\tchargement=true;\n";
327        $code .= "\tverifier_saisies_".$id_form." = function(form){\n";
328        foreach ($saisies as $saisie) {
329                // on utilise comme selecteur l'identifiant de saisie en priorite s'il est connu
330                // parce que conteneur_class = 'tableau[nom][option]' ne fonctionne evidement pas
331                // lorsque le name est un tableau
332                if (isset($saisie['options']['afficher_si'])) {
333                        ++$i;
334                        // Les [] dans le nom de la saisie sont transformés en _ dans le
335                        // nom de la classe, il faut faire pareil
336                        $nom_underscore = rtrim(
337                                        preg_replace('/[][]\[?/', '_', $saisie['options']['nom']),
338                                        '_'
339                        );
340                        // retrouver la classe css probable
341                        switch ($saisie['saisie']) {
342                                case 'fieldset':
343                                        $class_li = 'fieldset_'.$nom_underscore;
344                                        break;
345                                case 'explication':
346                                        $class_li = 'explication_'.$nom_underscore;
347                                        break;
348                                default:
349                                        // Les [] dans le nom de la saisie sont transformés en _ dans le
350                                        // nom de la classe, il faut faire pareil
351                                        $class_li = 'editer_'.$nom_underscore;
352                        }
353                        $condition = isset($saisie['options']['afficher_si']) ? $saisie['options']['afficher_si'] : '';
354                        // retrouver l'identifiant
355                        $identifiant = '';
356                        if (isset($saisie['identifiant']) and $saisie['identifiant']) {
357                                $identifiant = $saisie['identifiant'];
358                        }
359                        // On gère le cas @plugin:non_plugin@
360                        preg_match_all('#@plugin:(.+)@#U', $condition, $matches);
361                        foreach ($matches[1] as $plug) {
362                                if (defined('_DIR_PLUGIN_'.strtoupper($plug))) {
363                                        $condition = preg_replace('#@plugin:'.$plug.'@#U', 'true', $condition);
364                                } else {
365                                        $condition = preg_replace('#@plugin:'.$plug.'@#U', 'false', $condition);
366                                }
367                        }
368                        // On gère le cas @config:plugin:meta@ suivi d'un test
369                        preg_match_all('#@config:(.+):(.+)@#U', $condition, $matches);
370                        foreach ($matches[1] as $plugin) {
371                                $config = lire_config($plugin);
372                                $condition = preg_replace('#@config:'.$plugin.':'.$matches[2][0].'@#U', '"'.$config[$matches[2][0]].'"', $condition);
373                        }
374                        // On transforme en une condition valide
375                        preg_match_all('#@(.+)@#U', $condition, $matches);
376                        foreach ($matches[1] as $nom) {
377                                switch ($saisies[$nom]['saisie']) {
378                                        case 'radio':
379                                        case 'oui_non':
380                                        case 'true_false':
381                                                $condition = preg_replace('#@'.preg_quote($nom).'@#U', '$(form).find("[name=\''.$nom.'\']:checked").val()', $condition);
382                                                break;
383                                        case 'case':
384                                                $condition = preg_replace('#@'.preg_quote($nom).'@#U', '($(form).find(".checkbox[name=\''.$nom.'\']").is(":checked") ? $(form).find(".checkbox[name=\''.$nom.'\']").val() : "")', $condition);
385                                                break;
386                                        case 'checkbox':
387                                                /**
388                                                 * Faire fonctionner @checkbox_xx@ == 'valeur' et @checkbox_xx@ != 'valeur'
389                                                 */
390                                                $condition = preg_replace('#@(.+)@\s*(==|(!)=)\s*(\'[^\']*\'|"[^"]*")#U', "@$1@ $3IN $4", $condition );
391                                                /**
392                                                 * Faire fonctionner @checkbox_xx@ IN 'valeur' ou @checkbox_xx@ !IN 'valeur'
393                                                 */
394                                                preg_match_all('#@(.+)@\s*(!IN|IN)\s*[\'"](.*)[\'"]#U', $condition, $matches3);
395                                                foreach ($matches3[3] as $key => $value) {
396                                                        $not = '';
397                                                        if ($matches3[2][$key] == '!IN') {
398                                                                $not = '!';
399                                                        }
400                                                        $values = explode(',', $value);
401                                                        $new_condition = $not.'(';
402                                                        foreach ($values as $key2 => $cond) {
403                                                                if ($key2 > 0) {
404                                                                        $new_condition .= ' || ';
405                                                                }
406                                                                $new_condition .= '($(form).find(".checkbox[name=\''.$nom.'[]\'][value='.$cond.']").is(":checked") ? $(form).find(".checkbox[name=\''.$nom.'[]\'][value='.$cond.']").val() : "") == "'.$cond.'"';
407                                                        }
408                                                        $new_condition .= ')';
409                                                        $condition = str_replace($matches3[0][$key], $new_condition, $condition);
410                                                }
411                                                break;
412                                        default:
413                                                $condition = preg_replace('#@'.preg_quote($nom).'@#U', '$(form).find("[name=\''.$nom.'\']").val()', $condition);
414                                }
415                        }
416                        if ($identifiant) {
417                                $sel = "[data-id='$identifiant']";
418                        } else {
419                                $sel = ".$class_li";
420                        }
421                        $code .= "\tif (".$condition.") {\n"
422                                                         .      "\t\t$(form).find(\"$sel\").show(400);\n";
423                        if (html5_permis()) {
424                        $pour_html_5 =  "$sel.obligatoire > input, "// si le afficher_si porte directement sur le input
425                                                        ."$sel .obligatoire > input, "// si le afficher_si porte sur le fieldset
426                                                        ."$sel.obligatoire > textarea, "// si le afficher_si porte directement sur le textearea
427                                                        ."$sel .obligatoire > textarea, "// si le afficher_si porte sur le fiedset
428                                                        ."$sel.obligatoire > select, "//si le afficher_si porte directement sur le select
429                                                        ."$sel .obligatoire > select";//si le afficher_si porte sur le fieldset
430                        $code .=        "\t\t$(form).find("
431                                                        .'"'."$pour_html_5\")".
432                                                        ".attr(\"required\",true);\n";
433                        }
434                        $code .=        "\t}\n";
435                        $code .= "\telse {\n";
436                        if (html5_permis()) {
437                                $code .= "\t\t$(form).find(\n\t\t\t"
438                                                        .'"'."$pour_html_5\")\n"
439                                                        ."\t\t.attr(".'"required"'.",false);\n";
440                        }
441                        $code .= "\t\tif (chargement==true) {\n"
442                                        ."\t\t\t$(form).find(\"$sel\").hide(400).css".'("display","none")'.";\n"
443                                        ."\t\t}\n"
444                                        ."\t\telse {\n"
445                                        ."\t\t\t$(form).find(\"$sel\").hide(400);\n"
446                                        ."\t\t};\n"
447                                        ."\t}\n";
448                }
449        }
450        $code .= "$(form).trigger('saisies_afficher_si_js_ok');\n";
451        $code .= "};\n";
452        $code .= "\t".'$("#afficher_si_'.$id_form.'").parents("form").each(function(){'."\n\t\t".'verifier_saisies_'.$id_form.'(this);});'."\n";
453        $code .= "\t".'$("#afficher_si_'.$id_form.'").parents("form").change(function(){'."\n\t\t".'verifier_saisies_'.$id_form.'(this);});'."\n";
454        $code .= "\tchargement=false;})\n";
455
456        if (!defined('_SAISIES_AFFICHER_SI_JS_LISIBLE')) {
457                define('_SAISIES_AFFICHER_SI_JS_LISIBLE', false);
458        }
459        if (!_SAISIES_AFFICHER_SI_JS_LISIBLE) {
460                // il suffit de régler cette constante à TRUE pour afficher le js de manière plus lisible (et moins sibyllin)
461                $code = str_replace("\n", '', $code); //concatener
462                $code = str_replace("\t", '', $code); //concatener
463        }
464        return $i > 0 ? $code : '';
465}
466
467/**
468 * Lorsque l'on affiche les saisies (#VOIR_SAISIES), les saisies ayant une option afficher_si
469 * et dont les conditions ne sont pas remplies doivent être retirées du tableau de saisies.
470 *
471 * Cette fonction sert aussi lors de la vérification des saisies avec saisies_verifier().
472 * À ce moment là, les saisies non affichées sont retirées de _request
473 * (on passe leur valeur à NULL).
474 *
475 * @param array      $saisies
476 *                            Tableau de descriptions de saisies
477 * @param array|null $env
478 *                            Tableau d'environnement transmis dans inclure/voir_saisies.html,
479 *                            NULL si on doit rechercher dans _request (pour saisies_verifier()).
480 *
481 * @return array
482 *               Tableau de descriptions de saisies
483 */
484function saisies_verifier_afficher_si($saisies, $env = null) {
485        // eviter une erreur par maladresse d'appel :)
486        if (!is_array($saisies)) {
487                return array();
488        }
489        foreach ($saisies as $cle => $saisie) {
490                if (isset($saisie['options']['afficher_si'])) {
491                        $condition = $saisie['options']['afficher_si'];
492                        // Si tentative de log malicieux, on rejete
493                        if (!saisies_verifier_securite_afficher_si($condition)) {
494                                spip_log("Afficher_si malicieuse : $condition", "saisies"._LOG_CRITIQUE);
495                                $condition = '$ok';
496                        }
497                        // Est-ce uniquement au remplissage?
498                        if (isset($saisie['options']['afficher_si_remplissage_uniquement'])
499                                and $saisie['options']['afficher_si_remplissage_uniquement']=='on'){
500                                $remplissage_uniquement = true;
501                        } else {
502                                $remplissage_uniquement = false;
503                        }
504
505                        // On gère le cas @plugin:non_plugin@
506                        preg_match_all('#@plugin:(.+)@#U', $condition, $matches);
507                        foreach ($matches[1] as $plug) {
508                                if (defined('_DIR_PLUGIN_'.strtoupper($plug))) {
509                                        $condition = preg_replace('#@plugin:'.$plug.'@#U', 'true', $condition);
510                                } else {
511                                        $condition = preg_replace('#@plugin:'.$plug.'@#U', 'false', $condition);
512                                }
513                        }
514                        // On gère le cas @config:plugin:meta@ suivi d'un test
515                        preg_match_all('#@config:(.+):(.+)@#U', $condition, $matches);
516                        foreach ($matches[1] as $plugin) {
517                                $config = lire_config($plugin);
518                                $condition = preg_replace('#@config:'.$plugin.':'.$matches[2][0].'@#U', '"'.$config[$matches[2][0]].'"', $condition);
519                        }
520                        // On transforme en une condition PHP valide
521                        $condition_originale = $condition;
522                        if (is_null($env)) {
523                                $condition = preg_replace('#@(.+)@#U', '_request(\'$1\')', $condition);
524                        } else {
525                                $condition = preg_replace('#@(.+)@#U', '$env["valeurs"][\'$1\']', $condition);
526                        }
527
528                        /**
529                         * Tester si la condition utilise des champs qui sont des tableaux
530                         * Si _request() ou $env["valeurs"] est un tableau, changer == et != par in_array et !in_array
531                         * TODO: c'est vraiment pas terrible comme fonctionnement
532                         */
533                        preg_match_all('/(_request\([\'"].*?[\'"]\)|\$env\[[\'"].*?[\'"]\]\[[\'"].*?[\'"]\])\s*(!=|==|IN|!IN)\s*[\'"](.*?)[\'"]/', $condition, $matches);
534                        foreach ($matches[1] as $key => $val) {
535                                eval('$requete = '.$val.';');
536                                //Pour eviter une fatale erreur si on évalue une chose qui devrait normalement être un tableau mais qui n'a pas été envoyé (type checkbox), si la chose en question est null, la transformer en tableau vide. Pareil c'est pas terrible.
537                                if (is_null($requete)) {
538                                        $requete = array();
539                                        //C'est un request, alors on va faire un set_request
540                                        if (strpos($val, '_request') === 0) {
541                                                $set_tableau = "set$val";
542                                                $set_tableau = str_replace(")",",array())",$set_tableau);
543                                        } elseif (strpos($val, '$env') === 0) {//C'est un tablau direct
544                                                $set_tableau = ("$val = array()");
545                                        }
546                                        if (isset($set_tableau)) {
547                                                eval("$set_tableau;");
548                                        }
549                                }
550                                if (is_array($requete)) {
551                                        $not = '>';
552                                        if (in_array($matches[2][$key], array('!=', '!IN'))) {
553                                                $not = '==';
554                                        }
555                                        $array = var_export(explode(',', $matches[3][$key]), true);
556                                        $condition = str_replace($matches[0][$key], "(count(array_intersect($val, $array)) $not 0)", $condition);
557                                }
558                        }
559                        // On vérifie que l'on a pas @toto@="valeur" qui fait planter l'eval(),
560                        // on annule cette condition dans ce cas pour éviter une erreur du type :
561                        // PHP Fatal error:  Can't use function return value in write context
562                        $type_condition = preg_replace('#@(.+)@#U', '', $condition_originale);
563                        if (trim($type_condition) != '=') {
564                                eval('$ok = '.$condition.';');
565                        }
566                        if (!$ok) {
567                                if ($remplissage_uniquement == false or is_null($env)) {
568                                        unset($saisies[$cle]);
569                                }
570                                if (is_null($env)) {
571                                        saisies_set_request_null_recursivement($saisie);
572                                }
573                        }
574                }
575                if (isset($saisies[$cle]['saisies'])) {
576                        // S'il s'agit d'un fieldset ou equivalent, verifier les sous-saisies
577                        $saisies[$cle]['saisies'] = saisies_verifier_afficher_si($saisies[$cle]['saisies'], $env);
578                }
579        }
580
581        return $saisies;
582}
583
584
585/**
586 * Pose un set_request null sur une saisie et toute ses sous-saisies.
587 * Utiliser notamment pour annuler toutes les sous saisies d'un fieldeset
588 * si le fieldset est masquée à cause d'un afficher_si.
589 * @param array $saisie
590**/
591function saisies_set_request_null_recursivement($saisie) {
592        set_request($saisie['options']['nom'], null);
593        if (isset($saisie['saisies'])) {
594                foreach ($saisie['saisies'] as $sous_saisie) {
595                        saisies_set_request_null_recursivement($sous_saisie);
596                }
597        }
598}
599
600/**
601 * Vérifie qu'on tente pas de faire executer du code PHP en utilisant afficher_si.
602 * Interdit les ; et les $ sauf si entre guillemets.
603 * @param string $condition
604 * @return bool true si usage légitime, false si tentative d'execution de code PHP
605 */
606function saisies_verifier_securite_afficher_si($condition) {
607        $interdits = array(";","$","sql","spip");
608        $presence = 0;
609        foreach ($interdits as $int) {
610                if (strstr($condition, $int)) {
611                        $presence++;
612                }
613        }
614        // pas de chaine interdite, a priori safe
615        if (!$presence) {
616                return true;
617        }
618
619        // pas de guillemet ou de @, alors ca pue
620        if (strstr($condition, '"') == false and strstr($condition, "'") == false and strstr($condition, "@") == false) {
621                return false;
622        }
623
624        $regexp = "#(?<guillemet>(^\\\)?(\"|'|@))(.*)(\k<guillemet>)#mU"; // trouver tout ce qu'il y entre guillemet, sauf si les guillemets sont échapés
625        $condition = preg_replace($regexp, "", $condition);//Supprimer tout ce qu'il y a entre guillement
626
627        // il reste encore des caractères interdit alors qu'on a enlevé les guillemets, ca pue vraiment
628        foreach ($interdits as $int) {
629                if (strstr($condition, $int)) {
630                        return false;
631                }
632        }
633        //Sinon c'est que c'est bon
634        return true;
635}
Note: See TracBrowser for help on using the repository browser.