[PLUGINS] ~maj globale
[lhc/web/www.git] / www / plugins / saisies / inc / saisies_afficher.php
1 <?php
2
3 /**
4 * Gestion de l'affichage des saisies.
5 *
6 * @return SPIP\Saisies\Afficher
7 **/
8
9 // Sécurité
10 if (!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 */
37 function 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 */
98 function 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 // Si env est définie dans les options ou qu'il y a des enfants, on ajoute tout l'environnement
138 if (isset($contexte['env']) or (isset($champ['saisies']) and is_array($champ['saisies']))) {
139 unset($contexte['env']);
140
141 // on sauve l'ancien environnement
142 // car les sous-saisies ne doivent pas être affectees
143 // par les modification sur l'environnement servant à generer la saisie mère
144 $contexte['_env'] = $env;
145
146 // À partir du moment où on passe tout l'environnement, il faut enlever certains éléments qui ne doivent absolument provenir que des options
147 unset($env['inserer_debut']);
148 unset($env['inserer_fin']);
149 $saisies_disponibles = saisies_lister_disponibles();
150 if (isset($saisies_disponibles[$contexte['type_saisie']]) and is_array($saisies_disponibles[$contexte['type_saisie']]['options'])) {
151 $options_a_supprimer = saisies_lister_champs($saisies_disponibles[$contexte['type_saisie']]['options']);
152 foreach ($options_a_supprimer as $option_a_supprimer) {
153 unset($env[$option_a_supprimer]);
154 }
155 }
156
157 $contexte = array_merge($env, $contexte);
158 } else {
159 // Sinon on ne sélectionne que quelques éléments importants
160 // On récupère la liste des erreurs
161 $contexte['erreurs'] = $env['erreurs'];
162 // On récupère la langue de l'objet si existante
163 if (isset($env['langue'])) {
164 $contexte['langue'] = $env['langue'];
165 }
166 // On ajoute toujours le bon self
167 $contexte['self'] = self();
168 }
169
170 // Dans tous les cas on récupère de l'environnement la valeur actuelle du champ
171 // Si le nom du champ est un tableau indexé, il faut parser !
172 if (isset($contexte['nom']) and preg_match('/([\w]+)((\[[\w]+\])+)/', $contexte['nom'], $separe)
173 and isset($env[$separe[1]])) {
174 $contexte['valeur'] = $env[$separe[1]];
175 preg_match_all('/\[([\w]+)\]/', $separe[2], $index);
176 // On va chercher au fond du tableau
177 foreach ($index[1] as $cle) {
178 $contexte['valeur'] = isset($contexte['valeur'][$cle]) ? $contexte['valeur'][$cle] : null;
179 }
180 } elseif (isset($contexte['nom']) and isset($env[$contexte['nom']])) {
181 // Sinon la valeur est juste celle du nom si elle existe
182 $contexte['valeur'] = $env[$contexte['nom']];
183 } else {
184 // Sinon rien
185 $contexte['valeur'] = null;
186 }
187
188 // Si ya des enfants on les remonte dans le contexte
189 if (isset($champ['saisies']) and is_array($champ['saisies'])) {
190 $contexte['saisies'] = $champ['saisies'];
191 }
192
193 // On génère la saisie
194 return recuperer_fond(
195 'saisies/_base',
196 $contexte
197 );
198 }
199
200 /**
201 * Génère une vue d'une saisie à partir d'un tableau la décrivant.
202 *
203 * @see saisies_generer_html()
204 *
205 * @param array $saisie
206 * Tableau de description d'une saisie
207 * @param array $env
208 * L'environnement, contenant normalement la réponse à la saisie
209 * @param array $env_obligatoire
210 * ???
211 *
212 * @return string
213 * Code HTML de la vue de la saisie
214 */
215 function saisies_generer_vue($saisie, $env = array(), $env_obligatoire = array()) {
216 // Si le paramètre n'est pas bon, on génère du vide
217 if (!is_array($saisie)) {
218 return '';
219 }
220
221 $contexte = array();
222
223 // On sélectionne le type de saisie
224 $contexte['type_saisie'] = $saisie['saisie'];
225
226 // Peut-être des transformations à faire sur les options textuelles
227 $options = $saisie['options'];
228 foreach ($options as $option => $valeur) {
229 if ($option == 'datas') {
230 // exploser une chaine datas en tableau (applique _T_ou_typo sur chaque valeur)
231 $options[$option] = saisies_chaine2tableau($valeur);
232 } else {
233 $options[$option] = _T_ou_typo($valeur, 'multi');
234 }
235 }
236
237 // On ajoute les options propres à la saisie
238 $contexte = array_merge($contexte, $options);
239
240 // Si env est définie dans les options ou qu'il y a des enfants, on ajoute tout l'environnement
241 if (isset($contexte['env']) or (isset($saisie['saisies']) and is_array($saisie['saisies']))) {
242 unset($contexte['env']);
243
244 // on sauve l'ancien environnement
245 // car les sous-saisies ne doivent pas être affectees
246 // par les modification sur l'environnement servant à generer la saisie mère
247 $contexte['_env'] = $env;
248
249 // À partir du moment où on passe tout l'environnement, il faut enlever
250 // certains éléments qui ne doivent absolument provenir que des options
251 $saisies_disponibles = saisies_lister_disponibles();
252
253 if (isset($saisies_disponibles[$contexte['type_saisie']]['options'])
254 and is_array($saisies_disponibles[$contexte['type_saisie']]['options'])) {
255 $options_a_supprimer = saisies_lister_champs($saisies_disponibles[$contexte['type_saisie']]['options']);
256 foreach ($options_a_supprimer as $option_a_supprimer) {
257 unset($env[$option_a_supprimer]);
258 }
259 }
260
261 $contexte = array_merge($env, $contexte);
262 }
263
264 // Dans tous les cas on récupère de l'environnement la valeur actuelle du champ
265
266 // On regarde en priorité s'il y a un tableau listant toutes les valeurs
267 if (!empty($env['valeurs']) and is_array($env['valeurs']) and isset($env['valeurs'][$contexte['nom']])) {
268 $contexte['valeur'] = $env['valeurs'][$contexte['nom']];
269 } elseif (preg_match('/([\w]+)((\[[\w]+\])+)/', $contexte['nom'], $separe)) {
270 // Si le nom du champ est un tableau indexé, il faut parser !
271 $contexte['valeur'] = $env[$separe[1]];
272 preg_match_all('/\[([\w]+)\]/', $separe[2], $index);
273 // On va chercher au fond du tableau
274 foreach ($index[1] as $cle) {
275 $contexte['valeur'] = $contexte['valeur'][$cle];
276 }
277 } else {
278 // Sinon la valeur est juste celle du nom
279 // certains n'ont pas de nom (fieldset)
280 $contexte['valeur'] = isset($env[$contexte['nom']]) ? $env[$contexte['nom']] : '';
281 }
282
283 // Si ya des enfants on les remonte dans le contexte
284 if (isset($saisie['saisies']) and is_array($saisie['saisies'])) {
285 $contexte['saisies'] = $saisie['saisies'];
286 }
287
288 if (is_array($env_obligatoire)) {
289 $contexte = array_merge($contexte, $env_obligatoire);
290 }
291
292 // On génère la saisie
293 return recuperer_fond(
294 'saisies-vues/_base',
295 $contexte
296 );
297 }
298
299 /**
300 * Génère, à partir d'un tableau de saisie le code javascript ajouté à la fin de #GENERER_SAISIES
301 * pour produire un affichage conditionnel des saisies ayant une option afficher_si ou afficher_si_remplissage.
302 *
303 * @param array $saisies
304 * Tableau de descriptions des saisies
305 * @param string $id_form
306 * Identifiant unique pour le formulaire
307 *
308 * @return text
309 * Code javascript
310 */
311 function saisies_generer_js_afficher_si($saisies, $id_form) {
312 $i = 0;
313 $saisies = saisies_lister_par_nom($saisies, true);
314 $code = '';
315 $code .= '(function($){';
316 $code .= '$(document).ready(function(){chargement=true;';
317 $code .= 'verifier_saisies_'.$id_form." = function(form){\n";
318 foreach ($saisies as $saisie) {
319 // on utilise comme selecteur l'identifiant de saisie en priorite s'il est connu
320 // parce que conteneur_class = 'tableau[nom][option]' ne fonctionne evidement pas
321 // lorsque le name est un tableau
322 if (isset($saisie['options']['afficher_si']) or isset($saisie['options']['afficher_si_remplissage'])) {
323 ++$i;
324 // retrouver la classe css probable
325 switch ($saisie['saisie']) {
326 case 'fieldset':
327 $class_li = 'fieldset_'.$saisie['options']['nom'];
328 break;
329 case 'explication':
330 $class_li = 'explication_'.$saisie['options']['nom'];
331 break;
332 default:
333 // Les [] dans le nom de la saisie sont transformés en _ dans le
334 // nom de la classe, il faut faire pareil
335 $class_li = 'editer_' . rtrim(
336 preg_replace('/[][]\[?/', '_', $saisie['options']['nom']),
337 '_'
338 );
339 }
340 $afficher_si = isset($saisie['options']['afficher_si']) ? $saisie['options']['afficher_si'] : '';
341 $afficher_si_remplissage = isset($saisie['options']['afficher_si_remplissage']) ? $saisie['options']['afficher_si_remplissage'] : '';
342 $condition = implode("\n", array_filter(array($afficher_si, $afficher_si_remplissage)));
343 // retrouver l'identifiant
344 $identifiant = '';
345 if (isset($saisie['identifiant']) and $saisie['identifiant']) {
346 $identifiant = $saisie['identifiant'];
347 }
348 // On gère le cas @plugin:non_plugin@
349 preg_match_all('#@plugin:(.+)@#U', $condition, $matches);
350 foreach ($matches[1] as $plug) {
351 if (defined('_DIR_PLUGIN_'.strtoupper($plug))) {
352 $condition = preg_replace('#@plugin:'.$plug.'@#U', 'true', $condition);
353 } else {
354 $condition = preg_replace('#@plugin:'.$plug.'@#U', 'false', $condition);
355 }
356 }
357 // On gère le cas @config:plugin:meta@ suivi d'un test
358 preg_match_all('#@config:(.+):(.+)@#U', $condition, $matches);
359 foreach ($matches[1] as $plugin) {
360 $config = lire_config($plugin);
361 $condition = preg_replace('#@config:'.$plugin.':'.$matches[2][0].'@#U', '"'.$config[$matches[2][0]].'"', $condition);
362 }
363 // On transforme en une condition valide
364 preg_match_all('#@(.+)@#U', $condition, $matches);
365 foreach ($matches[1] as $nom) {
366 switch ($saisies[$nom]['saisie']) {
367 case 'radio':
368 case 'oui_non':
369 case 'true_false':
370 $condition = preg_replace('#@'.preg_quote($nom).'@#U', '$(form).find("[name=\''.$nom.'\']:checked").val()', $condition);
371 break;
372 case 'case':
373 $condition = preg_replace('#@'.preg_quote($nom).'@#U', '($(form).find(".checkbox[name=\''.$nom.'\']").is(":checked") ? $(form).find(".checkbox[name=\''.$nom.'\']").val() : "")', $condition);
374 break;
375 case 'checkbox':
376 preg_match_all('#@(.+)@\s*==\s*"(.*)"$#U', $condition, $matches2);
377 foreach ($matches2[2] as $value) {
378 $condition = preg_replace('#@'.preg_quote($nom).'@#U', '($(form).find(".checkbox[name=\''.$nom.'[]\'][value='.$value.']").is(":checked") ? $(form).find(".checkbox[name=\''.$nom.'[]\'][value='.$value.']").val() : "")', $condition);
379 }
380 break;
381 default:
382 $condition = preg_replace('#@'.preg_quote($nom).'@#U', '$(form).find("[name=\''.$nom.'\']").val()', $condition);
383 }
384 }
385 if ($identifiant) {
386 $sel = "[data-id='$identifiant']";
387 } else {
388 $sel = ".$class_li";
389 }
390 $code .= "\tif (".$condition.') {$(form).find("'.$sel.'").show(400);} '."\n\t";
391 $code .= 'else {if (chargement==true) {$(form).find("'.$sel.'").hide(400).css("display","none");} else {$(form).find("'.$sel.'").hide(400);};} '."\n";
392 }
393 }
394 $code .= '};';
395 $code .= '$("#afficher_si_'.$id_form.'").parents("form").each(function(){verifier_saisies_'.$id_form.'(this);});';
396 $code .= '$("#afficher_si_'.$id_form.'").parents("form").change(function(){verifier_saisies_'.$id_form.'(this);});';
397 $code .= 'chargement=false;})';
398 $code .= '})(jQuery);';
399
400 return $i > 0 ? $code : '';
401 }
402
403 /**
404 * Lorsque l'on affiche les saisies (#VOIR_SAISIES), les saisies ayant une option afficher_si
405 * et dont les conditions ne sont pas remplies doivent être retirées du tableau de saisies.
406 *
407 * Cette fonction sert aussi lors de la vérification des saisies avec saisies_verifier().
408 * À ce moment là, les saisies non affichées sont retirées de _request
409 * (on passe leur valeur à NULL).
410 *
411 * @param array $saisies
412 * Tableau de descriptions de saisies
413 * @param array|null $env
414 * Tableau d'environnement transmis dans inclure/voi_saisies.html,
415 * NULL si on doit rechercher dans _request (pour saisies_verifier()).
416 *
417 * @return array
418 * Tableau de descriptions de saisies
419 */
420 function saisies_verifier_afficher_si($saisies, $env = null) {
421 // eviter une erreur par maladresse d'appel :)
422 if (!is_array($saisies)) {
423 return array();
424 }
425 foreach ($saisies as $cle => $saisie) {
426 if (isset($saisie['options']['afficher_si'])) {
427 $condition = $saisie['options']['afficher_si'];
428 // On gère le cas @plugin:non_plugin@
429 preg_match_all('#@plugin:(.+)@#U', $condition, $matches);
430 foreach ($matches[1] as $plug) {
431 if (defined('_DIR_PLUGIN_'.strtoupper($plug))) {
432 $condition = preg_replace('#@plugin:'.$plug.'@#U', 'true', $condition);
433 } else {
434 $condition = preg_replace('#@plugin:'.$plug.'@#U', 'false', $condition);
435 }
436 }
437 // On gère le cas @config:plugin:meta@ suivi d'un test
438 preg_match_all('#@config:(.+):(.+)@#U', $condition, $matches);
439 foreach ($matches[1] as $plugin) {
440 $config = lire_config($plugin);
441 $condition = preg_replace('#@config:'.$plugin.':'.$matches[2][0].'@#U', '"'.$config[$matches[2][0]].'"', $condition);
442 }
443 // On transforme en une condition valide
444 if (is_null($env)) {
445 $condition = preg_replace('#@(.+)@#U', '_request(\'$1\')', $condition);
446 } else {
447 $condition = preg_replace('#@(.+)@#U', '$env["valeurs"][\'$1\']', $condition);
448 }
449 eval('$ok = '.$condition.';');
450 if (!$ok) {
451 unset($saisies[$cle]);
452 if (is_null($env)) {
453 set_request($saisie['options']['nom'], null);
454 }
455 }
456 }
457 if (isset($saisies[$cle]['saisies'])) { // S'il s'agit d'un fieldset ou equivalent, verifier les sous-saisies
458 $saisies[$cle]['saisies'] = saisies_verifier_afficher_si($saisies[$cle]['saisies'], $env);
459 }
460 }
461
462 return $saisies;
463 }