source: spip-zone/_plugins_/saisies/trunk/inc/saisies_lister.php @ 114384

Last change on this file since 114384 was 114384, checked in by rastapopoulos@…, 9 months ago

Nouveauté sans rien casser (normalement) : l'API saisies permet maintenant d'activer un formulaire multi-étapes en une ligne ! Pour ça, il suffit 1) d'avoir des fieldsets racines qui se transformeront en étapes et 2) d'avoir un tableau options dans le tableau des saisies avec 'options'=>array('etapes_activer'=>true). Et… c'est tout ! Paf magie. Comme option, on peut aussi utiliser 'etapes_suivant' et 'etapes_precedent' pour personnaliser les labels des boutons qui s'affichent quand on n'est pas encore à la fin du formulaire. Ça ajoute aussi un menu pas trop mal accessible (ça suit accedeweb) qui liste les étapes avec leur nom humain (le label des fieldsets) et quand ce sont les étapes précédentes, ça fait un bouton pour y revenir. La suite : permettre aussi de configurer tout ça par interface dans le constructeur de formulaire, afin de pouvoir l'utiliser dans Formidable ! Demain est un autre jour…

File size: 16.7 KB
Line 
1<?php
2
3/**
4 * Gestion de listes des saisies.
5 *
6 * @return SPIP\Saisies\Listes
7 **/
8
9// Sécurité
10if (!defined('_ECRIRE_INC_VERSION')) {
11        return;
12}
13
14/**
15 * Prend la description complète du contenu d'un formulaire et retourne
16 * les saisies "à plat" classées par identifiant unique.
17 *
18 * @param array $contenu        Le contenu d'un formulaire
19 * @param bool  $avec_conteneur Indique si on renvoie aussi les saisies ayant des enfants, comme les fieldsets
20 *
21 * @return array Un tableau avec uniquement les saisies
22 */
23function saisies_lister_par_identifiant($contenu, $avec_conteneur = true) {
24        $saisies = array();
25
26        if (is_array($contenu)) {
27                foreach ($contenu as $ligne) {
28                        if (is_array($ligne)) {
29                                $enfants_presents = (isset($ligne['saisies']) and is_array($ligne['saisies']));
30                                if (array_key_exists('saisie', $ligne) and (!$enfants_presents or $avec_conteneur)) {
31                                        $saisies[$ligne['identifiant']] = $ligne;
32                                }
33                                if ($enfants_presents) {
34                                        $saisies = array_merge($saisies, saisies_lister_par_identifiant($ligne['saisies']));
35                                }
36                        }
37                }
38        }
39
40        return $saisies;
41}
42
43/**
44 * Prend la description complète du contenu d'un formulaire et retourne
45 * les saisies "à plat" classées par nom.
46 *
47 * @param array $contenu        Le contenu d'un formulaire
48 * @param bool  $avec_conteneur Indique si on renvoie aussi les saisies ayant des enfants, comme les fieldset
49 *
50 * @return array Un tableau avec uniquement les saisies
51 */
52function saisies_lister_par_nom($contenu, $avec_conteneur = true) {
53        $saisies = array();
54
55        if (is_array($contenu)) {
56                foreach ($contenu as $ligne) {
57                        if (is_array($ligne)) {
58                                if (
59                                        array_key_exists('saisie', $ligne)
60                                        and (!isset($ligne['saisies']) or !is_array($ligne['saisies']) or $avec_conteneur)
61                                        and isset($ligne['options'])
62                                ) {
63                                        $saisies[$ligne['options']['nom']] = $ligne;
64                                }
65                                if (isset($ligne['saisies']) and is_array($ligne['saisies'])) {
66                                        $saisies = array_merge($saisies, saisies_lister_par_nom($ligne['saisies']));
67                                }
68                        }
69                }
70        }
71
72        return $saisies;
73}
74
75/**
76 * Liste les saisies ayant une option X
77 * # saisies_lister_avec_option('sql', $saisies);.
78 *
79 *
80 * @param string $option  Nom de l'option cherchée
81 * @param array  $saisies Liste de saisies
82 * @param string $tri     tri par défaut des résultats (s'ils ne sont pas deja triés) ('nom', 'identifiant')
83 *
84 * @return liste de ces saisies triees par nom ayant une option X définie
85 */
86function saisies_lister_avec_option($option, $saisies, $tri = 'nom') {
87        $saisies_option = array();
88
89        // tri par nom si ce n'est pas le cas
90        $s = array_keys($saisies);
91        if (is_int(array_shift($s))) {
92                $trier = 'saisies_lister_par_'.$tri;
93                $saisies = $trier($saisies);
94        }
95
96        foreach ($saisies as $nom_ou_id => $saisie) {
97                if (isset($saisie['options'][$option]) and $saisie['options'][$option]) {
98                        $saisies_option[$nom_ou_id] = $saisie;
99                }
100        }
101
102        return $saisies_option;
103}
104
105/**
106 * Liste les saisies ayant une definition SQL.
107 *
108 * @param array  $saisies liste de saisies
109 * @param string $tri     tri par défaut des résultats (s'ils ne sont pas deja triés) ('nom', 'identifiant')
110 *
111 * @return liste de ces saisies triees par nom ayant une option sql définie
112 */
113function saisies_lister_avec_sql($saisies, $tri = 'nom') {
114        return saisies_lister_avec_option('sql', $saisies, $tri);
115}
116
117/**
118 * Liste les saisies d'un certain type.
119 *
120 * @example `$saisies_date = saisies_lister_avec_type($saisies, 'date')`
121 *
122 * @param array  $saisies liste de saisies
123 * @param string|array $type    Type de la saisie, ou tableau de types
124 * @param string $tri     tri par défaut des résultats (s'ils ne sont pas deja triés) ('nom')
125 *
126 * @return liste de ces saisies triees par nom
127 */
128function saisies_lister_avec_type($saisies, $type, $tri = 'nom') {
129        if (!is_array($type)) {
130                $type = array($type);
131        }
132        $saisies_type = array();
133
134        // tri par nom si ce n'est pas le cas
135        $s = array_keys($saisies);
136        if (is_int(array_shift($s))) {
137                $trier = 'saisies_lister_par_'.$tri;
138                $saisies = $trier($saisies);
139        }
140
141        foreach ($saisies as $nom_ou_id => $saisie) {
142                if (in_array($saisie['saisie'], $type)) {
143                        $saisies_type[$nom_ou_id] = $saisie;
144                }
145        }
146        return $saisies_type;
147}
148
149/**
150 * Prend la description complète du contenu d'un formulaire et retourne
151 * les saisies "à plat" classées par type de saisie.
152 * $saisie['input']['input_1'] = $saisie.
153 *
154 * @param array $contenu Le contenu d'un formulaire
155 *
156 * @return array Un tableau avec uniquement les saisies
157 */
158function saisies_lister_par_type($contenu) {
159        $saisies = array();
160
161        if (is_array($contenu)) {
162                foreach ($contenu as $ligne) {
163                        if (is_array($ligne)) {
164                                if (array_key_exists('saisie', $ligne) and (!is_array($ligne['saisies']))) {
165                                        $saisies[ $ligne['saisie'] ][ $ligne['options']['nom'] ] = $ligne;
166                                }
167                                if (is_array($ligne['saisies'])) {
168                                        $saisies = array_merge_recursive($saisies, saisies_lister_par_type($ligne['saisies']));
169                                }
170                        }
171                }
172        }
173
174        return $saisies;
175}
176
177/**
178 * Liste les saisies par étapes s'il y en a
179 *
180 * @param array $saisies
181 *              Liste des saisies
182 * @return array|bool
183 *              Retourne un tableau associatif par numéro d'étape avec pour chacune leurs saisies, false si pas d'étapes
184 */
185function saisies_lister_par_etapes($saisies) {
186        $saisies_etapes = false;
187        $etapes = 0;
188       
189        if (isset($saisies['options']['etapes_activer']) and $saisies['options']['etapes_activer']) {
190                // Un premier parcourt pour compter les étapes
191                foreach ($saisies as $cle => $saisie) {
192                        if (is_array($saisies) and $saisie['saisie'] == 'fieldset') {
193                                $etapes++;
194                        }
195                }
196               
197                // Seulement s'il y a au moins deux étapes
198                if ($etapes > 1) {
199                        $saisies_etapes = array();
200                        $compteur_etape = 0;
201                       
202                        // On reparcourt pour lister les saisies
203                        foreach ($saisies as $cle => $saisie) {
204                                // Si c'est un groupe, on ajoute son contenu à l'étape
205                                if (isset($saisie['saisie']) and $saisie['saisie'] == 'fieldset') {
206                                        $compteur_etape++;
207                                        // S'il y a eu des champs hors groupe avant, on fusionne
208                                        if (isset($saisies_etapes[$compteur_etape]['saisies'])) {
209                                                $saisies_precedentes = $saisies_etapes[$compteur_etape]['saisies'];
210                                                $saisies_etapes[$compteur_etape] = $saisie;
211                                                $saisies_etapes[$compteur_etape]['saisies'] = array_merge($saisies_precedentes, $saisie['saisies']);
212                                        }
213                                        else {
214                                                $saisies_etapes[$compteur_etape] = $saisie;
215                                        }
216                                }
217                                // Sinon si champ externe à un groupe, on l'ajoute à toutes les étapes
218                                elseif (isset($saisie['saisie'])) {
219                                        for ($e=1;$e<=$etapes;$e++) {
220                                                if (!isset($saisies_etapes[$e]['saisies'])) {
221                                                        $saisies_etapes[$e] = array('saisies'=>array());
222                                                }
223                                                array_push($saisies_etapes[$e]['saisies'], $saisie);
224                                        }
225                                }
226                        }
227                }
228        }
229       
230        return $saisies_etapes;
231}
232
233/**
234 * Prend la description complète du contenu d'un formulaire et retourne
235 * une liste des noms des champs du formulaire.
236 *
237 * @param array $contenu        Le contenu d'un formulaire
238 * @param bool  $avec_conteneur Indique si on renvoie aussi les saisies ayant des enfants, comme les fieldset
239 *
240 * @return array Un tableau listant les noms des champs
241 */
242function saisies_lister_champs($contenu, $avec_conteneur = true) {
243        $saisies = saisies_lister_par_nom($contenu, $avec_conteneur);
244
245        return array_keys($saisies);
246}
247
248/**
249 * Prend la description complète du contenu d'un formulaire et retourne
250 * une liste des labels humains des vrais champs du formulaire (par nom)
251 *
252 * @param array $contenu        Le contenu d'un formulaire
253 * @param bool  $avec_conteneur Indique si on renvoie aussi les saisies ayant des enfants, comme les fieldset
254 *
255 * @return array Un tableau listant les labels humains des champs
256 */
257function saisies_lister_labels($contenu, $avec_conteneur = false) {
258        $saisies = saisies_lister_par_nom($contenu, $avec_conteneur);
259
260        $labels = array();
261        foreach ($saisies as $nom => $saisie) {
262                if (isset($saisie['options']['label'])) {
263                        $labels[$nom] = $saisie['options']['label'];
264                }
265        }
266
267        return $labels;
268}
269
270/**
271 * A utiliser dans une fonction charger d'un formulaire CVT,
272 * cette fonction renvoie le tableau de contexte correspondant
273 * de la forme $contexte['nom_champ'] = ''.
274 *
275 * @param array $contenu Le contenu d'un formulaire (un tableau de saisies)
276 *
277 * @return array Un tableau de contexte
278 */
279function saisies_charger_champs($contenu) {
280        if (function_exists('array_fill_keys')) { // php 5.2
281                return array_fill_keys(saisies_lister_champs($contenu, false), null);
282        }
283        $champs = array();
284        foreach (saisies_lister_champs($contenu, false) as $champ) {
285                $champs[$champ] = null;
286        }
287
288        return $champs;
289}
290
291/**
292 * Prend la description complète du contenu d'un formulaire et retourne
293 * une liste des valeurs par défaut des champs du formulaire.
294 *
295 * @param array $contenu Le contenu d'un formulaire
296 *
297 * @return array Un tableau renvoyant la valeur par défaut de chaque champs
298 */
299function saisies_lister_valeurs_defaut($contenu) {
300        $contenu = saisies_lister_par_nom($contenu, false);
301        $defauts = array();
302
303        foreach ($contenu as $nom => $saisie) {
304                // Si le nom du champ est un tableau indexé, il faut parser !
305                if (preg_match('/([\w]+)((\[[\w]+\])+)/', $nom, $separe)) {
306                        $nom = $separe[1];
307                        // Dans ce cas on ne récupère que le nom,
308                        // la valeur par défaut du tableau devra être renseigné autre part
309                        $defauts[$nom] = array();
310                }
311                else {
312                        $defauts[$nom] = isset($saisie['options']['defaut']) ? $saisie['options']['defaut'] : '';
313                }
314        }
315
316        return $defauts;
317}
318
319/**
320 * Compare deux tableaux de saisies pour connaitre les différences.
321 *
322 * @param array  $saisies_anciennes Un tableau décrivant des saisies
323 * @param array  $saisies_nouvelles Un autre tableau décrivant des saisies
324 * @param bool   $avec_conteneur    Indique si on veut prendre en compte dans la comparaison les conteneurs comme les fieldsets
325 * @param string $tri               Comparer selon quel tri ? 'nom' / 'identifiant'
326 *
327 * @return array Retourne le tableau des saisies supprimées, ajoutées et modifiées
328 */
329function saisies_comparer($saisies_anciennes, $saisies_nouvelles, $avec_conteneur = true, $tri = 'nom') {
330        $trier = "saisies_lister_par_$tri";
331        $saisies_anciennes = $trier($saisies_anciennes, $avec_conteneur);
332        $saisies_nouvelles = $trier($saisies_nouvelles, $avec_conteneur);
333
334        // Les saisies supprimées sont celles qui restent dans les anciennes quand on a enlevé toutes les nouvelles
335        $saisies_supprimees = array_diff_key($saisies_anciennes, $saisies_nouvelles);
336        // Les saisies ajoutées, c'est le contraire
337        $saisies_ajoutees = array_diff_key($saisies_nouvelles, $saisies_anciennes);
338        // Il reste alors les saisies qui ont le même nom
339        $saisies_restantes = array_intersect_key($saisies_anciennes, $saisies_nouvelles);
340        // Dans celles-ci, celles qui sont modifiées sont celles dont la valeurs est différentes
341        $saisies_modifiees = array_udiff(array_diff_key($saisies_nouvelles, $saisies_ajoutees), $saisies_restantes, 'saisies_comparer_rappel');
342        #$saisies_modifiees = array_udiff($saisies_nouvelles, $saisies_restantes, 'saisies_comparer_rappel');
343        // Et enfin les saisies qui ont le même nom et la même valeur
344        $saisies_identiques = array_diff_key($saisies_restantes, $saisies_modifiees);
345
346        return array(
347                'supprimees' => $saisies_supprimees,
348                'ajoutees' => $saisies_ajoutees,
349                'modifiees' => $saisies_modifiees,
350                'identiques' => $saisies_identiques,
351        );
352}
353
354/**
355 * Compare deux saisies et indique si elles sont égales ou pas.
356 *
357 * @param array $a Une description de saisie
358 * @param array $b Une autre description de saisie
359 *
360 * @return int Retourne 0 si les saisies sont identiques, 1 sinon.
361 */
362function saisies_comparer_rappel($a, $b) {
363        if ($a === $b) {
364                return 0;
365        } else {
366                return 1;
367        }
368}
369
370/**
371 * Compare deux tableaux de saisies pour connaitre les différences
372 * en s'appuyant sur les identifiants de saisies.
373 *
374 * @see saisies_comparer()
375 *
376 * @param array $saisies_anciennes Un tableau décrivant des saisies
377 * @param array $saisies_nouvelles Un autre tableau décrivant des saisies
378 * @param bool  $avec_conteneur    Indique si on veut prendre en compte dans la comparaison
379 *                                 les conteneurs comme les fieldsets
380 *
381 * @return array Retourne le tableau des saisies supprimées, ajoutées et modifiées
382 */
383function saisies_comparer_par_identifiant($saisies_anciennes, $saisies_nouvelles, $avec_conteneur = true) {
384        return saisies_comparer($saisies_anciennes, $saisies_nouvelles, $avec_conteneur, 'identifiant');
385}
386
387/**
388 * Liste toutes les saisies configurables (ayant une description).
389 *
390 * @return array Un tableau listant des saisies et leurs options
391 */
392function saisies_lister_disponibles($saisies_repertoire = 'saisies') {
393        static $saisies = null;
394
395        if (is_null($saisies)) {
396                $saisies = array();
397                $liste = find_all_in_path("$saisies_repertoire/", '.+[.]yaml$');
398
399                if (count($liste)) {
400                        foreach ($liste as $fichier => $chemin) {
401                                $type_saisie = preg_replace(',[.]yaml$,i', '', $fichier);
402                                $dossier = str_replace($fichier, '', $chemin);
403
404                                // On ne garde que les saisies qui ont bien le HTML avec !
405                                if (
406                                        file_exists("$dossier$type_saisie.html")
407                                        and (
408                                                is_array($saisie = saisies_charger_infos($type_saisie))
409                                        )
410                                ) {
411                                        $saisies[$type_saisie] = $saisie;
412                                }
413                        }
414                }
415        }
416
417        return $saisies;
418}
419
420/**
421 * Liste tous les groupes de saisies configurables (ayant une description).
422 *
423 * @return array Un tableau listant des saisies et leurs options
424 */
425function saisies_groupes_lister_disponibles($saisies_repertoire = 'saisies') {
426        static $saisies = null;
427
428        if (is_null($saisies)) {
429                $saisies = array();
430                $liste = find_all_in_path("$saisies_repertoire/", '.+[.]yaml$');
431
432                if (count($liste)) {
433                        foreach ($liste as $fichier => $chemin) {
434                                $type_saisie = preg_replace(',[.]yaml$,i', '', $fichier);
435                                $dossier = str_replace($fichier, '', $chemin);
436
437                                if (is_array($saisie = saisies_charger_infos($type_saisie, $saisies_repertoire))) {
438                                        $saisies[$type_saisie] = $saisie;
439                                }
440                        }
441                }
442        }
443
444        return $saisies;
445}
446
447/**
448 * Lister les saisies existantes ayant une définition SQL.
449 *
450 * @return array Un tableau listant des saisies et leurs options
451 */
452function saisies_lister_disponibles_sql($saisies_repertoire = 'saisies') {
453        $saisies = array();
454        $saisies_disponibles = saisies_lister_disponibles($saisies_repertoire);
455
456        foreach ($saisies_disponibles as $type => $saisie) {
457                if (isset($saisie['defaut']['options']['sql']) and $saisie['defaut']['options']['sql']) {
458                        $saisies[$type] = $saisie;
459                }
460        }
461
462        return $saisies;
463}
464
465/**
466 * Charger les informations contenues dans le YAML d'une saisie.
467 *
468 * @param string $type_saisie Le type de la saisie
469 *
470 * @return array Un tableau contenant le YAML décodé
471 */
472function saisies_charger_infos($type_saisie, $saisies_repertoire = 'saisies') {
473        if (defined('_DIR_PLUGIN_YAML')) {
474                include_spip('inc/yaml');
475                $fichier = find_in_path("$saisies_repertoire/$type_saisie.yaml");
476                $saisie = yaml_decode_file($fichier);
477
478                if (is_array($saisie)) {
479                        $saisie['titre'] = (isset($saisie['titre']) and $saisie['titre'])
480                                ? _T_ou_typo($saisie['titre']) : $type_saisie;
481                        $saisie['description'] = (isset($saisie['description']) and $saisie['description'])
482                                ? _T_ou_typo($saisie['description']) : '';
483                        $saisie['icone'] = (isset($saisie['icone']) and $saisie['icone'])
484                                ? find_in_path($saisie['icone']) : '';
485                }
486        }
487        else {
488                $saisie = array();
489        }
490
491        return $saisie;
492}
493
494/**
495 * Quelles sont les saisies qui se débrouillent toutes seules, sans le _base commun.
496 *
497 * @return array Retourne un tableau contenant les types de saisies qui ne doivent pas utiliser le _base.html commun
498 */
499function saisies_autonomes() {
500        $saisies_autonomes = pipeline(
501                'saisies_autonomes',
502                array(
503                        'fieldset',
504                        'hidden',
505                        'destinataires',
506                        'explication',
507                )
508        );
509
510        return $saisies_autonomes;
511}
512
513/**
514 * La saisie renvoie t-elle un tableau?
515 * note: on teste saisie par saisie, et non pas type de saisie par type de saisie, car certaine type (Evenements par ex) peut, en fonction des options, être tabulaire ou pas.
516 * @param $saisie
517 * @return return bool true si la saisie est tabulaire, false sinon
518**/
519function saisies_saisie_est_tabulaire($saisie) {
520        if (in_array($saisie['saisie'], array('checkbox', 'selection_multiple'))) {
521                $est_tabulaire = true;
522        } else {
523                $est_tabulaire =  false;
524        }
525        return pipeline('saisie_est_tabulaire',
526                array('args' => $saisie, 'data' => $est_tabulaire)
527        );
528}
529
530/**
531 * Indique si une saisie à sa valeur gelée
532 * - soit par option disabled avec envoi cachée
533 * - soit par option readonly
534 * @param array $description description de la saisie
535 * @return bool true si gélée, false sinon)
536**/
537function saisies_verifier_gel_saisie($description) {
538        $options = $description['options'];
539        //As t-on bloqué d'une manière ou d'une autre la valeur postée?
540        if ((
541                isset($options['readonly'])
542                and $options['readonly']
543        )
544        or (
545                isset($options['disable'])
546                and isset($options['disable_avec_post'])
547                and $options['disable']
548                and $options['disable_avec_post']
549        )
550        ) {
551                return true;
552        } else {
553                return false;
554        }
555}
Note: See TracBrowser for help on using the repository browser.