Créer ses propres saisies

Cette documentation explique comment créer ses propres saisies pour le plugin Saisies. Elle est valable à partir de la version 4.6.0 du plugin pour SPIP ≥ 4.0.

On pourra consulter les saisies fournies avec le plugin pour compléter cette documentation.

Tous les chemins de fichier ou dossier indiqués correspondent à la notion de chemin SPIP.

Contenu de base d’une saisie

Une saisie se compose, basiquement, de trois éléments :
-  l’élément indispensable est le contrôleur, il s’agit d’un squelette indiquant comment doit s’afficher la saisie dans un formulaire ;
-  on peut lui adjoindre une vue, il s’agit d’un squelette indiquant comment présenter le résultat de la saisie ;
-  on peut également lui adjoindre un constructeur, il s’agit d’un fichier .yaml permettant d’expliquer comment configurer la saisie dans les formulaires permettant de construire des formulaires, par exemple dans Formidable.

Un contrôleur

Définition

Le contrôleur est un squelette <saisie>.html à placer dans un dossier saisies.

Ce squelette génère le code html de la saisie. Sauf exception, il ne doit pas contenir le label et les messages d’erreur, ni l’élèment HTML englobant (<div> ou <li> selon la version de SPIP) . Ces éléments sont ajoutés automatiquement via le squelette saisies/_base.html pour la plupart des saisies. Il est toutefois possible de créer des saisies autonomes qui ne dépendent pas de saisies/_base.html.

Comme tout squelette SPIP, il peut utiliser des boucles, balise, etc. Les réglages de la saisie sont disponibles dans le #ENV.

Exemple

Voici le contrôleur de la saisie input, c’est-à-dire le squelette saisies/input.html.

[(#REM)

  Parametres supplementaire :
  - ** data : tableau de donnees indice=>valeur
  - defaut : valeur par defaut du parametre
  - type : type de l'input (defaut: text)
  - class : classe(s) css ajoutes a l'input
  - size : taille du champ
  - maxlength : nombre de caracteres maximum
  - disable : champ insaisissable ? 'oui' (defaut : '')
  - valeur_forcee : valeur utilisee meme si une valeur est dans l'environnement
  - autofocus : indique si le champ prend le focus a l'affichage (HTML5 requis)
  - placeholder : texte du placeholder
  - cle_secrete : l'input est une cle secrete. On n'affiche pas la valeur mais simplement un placeholder indiquant les premiers élèments de la clef secrete


  Exemple d'appel :
	[(#SAISIE{input,couleur_foncee,
		label=<:spa:couleur_foncee:>,
		size=7,
		data=#ARRAY{
				0,valeur0,
				1,valeur1,
				2,valeur2}})]
]

#SET{type,#ENV{type,text}}

[(#REM) data peut être une chaine qu'on sait décomposer ]
#SET{data, #ENV*{data, #ENV*{datas}}}
#SET{data, #GET{data}|saisies_chaine2tableau}

[(#REM) Pas de HTML5, pas de data]
#SET{data, #HTML5|?{#GET{data}}}

[(#REM)  l'attribut autocomplete ne peut avoir pour valeur que on ou off ]
#SET{val_autocomplete, #ARRAY}
#SET{val_autocomplete, #GET{val_autocomplete}|push{on}}
#SET{val_autocomplete, #GET{val_autocomplete}|push{off}}

[(#REM) cle secrete > on modifie le place holder]
#SET{placeholder,''}
[(#ENV{cle_secrete}|oui)
	#SET{placeholder,#ENV{valeur_forcee,#ENV{valeur,#ENV{{default}}}|saisies_masquer_cle_secrete}
]
[(#ENV{cle_secrete}|non)
	#SET{placeholder,#ENV{placeholder}}
]

[(#REM) permettre de donner un identifiant de list specifique en option de la saisie
]#SET{list_id,#ENV{list}}
[(#REM) Détecter si le tableau est séquentiel ou associatif, pour choisir s'il faut prendre la clé en valeur
]#SET{data_is_sequential,#GET{data}|is_array|?{#GET{data}|array_keys|=={#VAL{0}|range{#GET{data}|count|moins{1}}}}}
<B_selection>
[(#SET{list_id,[(#GET{list_id,[champ_(#ENV{nom})_data]})]})]
<datalist id="#GET{list_id}">
<BOUCLE_selection(POUR){tableau #GET{data}}>
  [<option value="(#GET{data_is_sequential}|?{#VALEUR,#CLE}|attribut_html)">[(#VALEUR|attribut_html)]</option>]
</BOUCLE_selection>
</datalist>
</B_selection>
<input type="#GET{type}" name="#ENV{nom}" class="#GET{type}[ (#ENV{class})]" id="champ_[(#ENV{id,#ENV{nom}}|saisie_nom2classe)]"[ list="(#GET{list_id})"][ value="(#ENV{cle_secrete}|?{'',#ENV{valeur_forcee,#ENV{valeur,#ENV{defaut}}}})"][ size="(#ENV{size})"][ maxlength="(#ENV{maxlength})"][ disabled="(#ENV{disable})"][ readonly="(#ENV{readonly})"][ placeholder="(#GET{placeholder})"][(#HTML5|oui)[(#ENV{obligatoire}|et{#ENV{obligatoire}|!={non}}|oui) required="required"][ min="(#ENV{min})"][ max="(#ENV{max})"][ step="(#ENV{step})"][(#ENV{autofocus}|et{#ENV{autofocus}|!={non}}|oui) autofocus="autofocus"]][(#GET{val_autocomplete}|find{#ENV{autocomplete}}|oui) autocomplete="#ENV{autocomplete}"][ aria-describedby="(#ENV{describedby})"][ (#ENV*{attributs})] />

Une vue (facultative)

Définition

La vue correspond à la manière dont est affiché le résultat d’une saisie. Par exemple, pour une saisie de type radio, il faut afficher les valeurs compréhensibles par les humaines et non pas la valeur technique brute.

La vue d’une saisie est un simple squelette présent dans saisies-vues/<saisie>.html. Le résultat de la saisie est présent dans #ENV{valeur}.

Comme pour le contrôleur, le label et le conteneur HTML global se trouve dans saisies-vues/_base.html et ne doit donc pas être inclus dans saisies-vues/<saisie>.html.

Exemple

Voici la vue de la saisie radio, dans le squelette saisies-vues/radio.html.

[(#REM) data peut être une chaine qu'on sait décomposer ]
#SET{data, #ENV*{data, #ENV*{datas}}|saisies_chaine2tableau}
[(#ENV{cle_ou_valeur,valeur}|=={cle}|?{
	[<p>(#ENV{valeur})</p>],
	[<p>(#GET{data/#ENV{valeur}})</p>]
})]

Un petit peu d’analyse :
-  #ENV{cle_ou_valeur} est un paramètre passé à la vue. Il permet dans certains cas (par exemple pour l’export de donnée sous forme de tableau) d’afficher la clé informatique plutôt que la valeur humaine ;
-  le filtre saisies_chaine2tableau permet de transformer les chaînes du type :

cle1|valeur1
cle2|valeur2

En tableau PHP du type :

array ('cle1' => 'valeur1', 'cle2' => 'valeur2')

C’est ce qui nous permet de retrouver ensuite la valeur humaine via #GET{data/#ENV{valeur}} [1].

Un constructeur (facultatif) .yaml

Définition

Le constructeur décrit formellement la saisie et ses options. Il est utilisé :

Le constructeur est fichier saisies/<saisie>.yaml décrivant la saisie sous forme d’un tableau associatif YAML.

Contenu du constructeur

Les clés du tableau sont les suivantes :
-  titre ;
-  description ;
-  icone icone représentant la saisie, à chercher d’abord dans le dossier images du thème, puis, à défaut, à traver find_in_path ;
-  categorie catégorie et position au sein de la catégorie, nous détaillons plus loins l’usage ;
-  options tableau décrivant les options possibles pour la saisie, nous détaillons plus loin l’usage ;
-  options_dev tableau décrivant les options possible pour la saisies mais qui ne sont accessibles que pour les dev (en PHP/squelettes), et pas à travers l’interface graphique ;
-  defaut réglages des options et des vérifications par défaut, nous détaillons plus loin l’usage.

D’autres clés sont possibles pour des usages avancés et sont décrites plus loin dans cet article.

Catégorie

Définition

Chaque saisie peut être affectée à une catégorie. À défaut, elle sera affectée à la catégorie defaut. Au sein d’une catégorie, les saisies sont classées dans l’ordre suivant :
-  par rang, si pas de rang défini, alors tout à la fin ;
-  puis pour deux catégories du même rang, par ordre alphabétique (sans les accents).

Les catégories livrées en standards sont les suivantes.

Catégories des saisies
Clé informatiqueLibellé humainExemple de saisie concernée
libre Champ libre input
choix Choix restreint radio
structure Structure fieldset
objet Contenu éditorial selecteur_article
defaut Divers hidden

Il est possible d’ajouter ses propres catégories via un pipeline.

Exemple

La saisie email possède les entrées suivantes dans son .yaml :

categorie:
  type: 'libre'
  rang: 10

Tableau d’options du constructeur

L’entrée options du constructeur décrit les options disponibles. Ces options seront configurables par des saisies dans l’interface de construction de formulaire. Par conséquent, le tableau d’options n’est ni plus ni moins qu’une liste de saisies.

Il est recommandé de le regrouper en fieldset. On veillera dans la mesure du possible à se conformer à la structure conventionnelle suivante :

options:
  -
    saisie: 'fieldset'
    options:
      nom: 'description'
      label: '<:saisies:option_groupe_description:>'
    saisies:
      <tableau de saisies pour les options décrivant la saisie (label, explication, data, etc.)>
  -
    saisie: 'fieldset'
    options:
      nom: 'utilisation'
      label: '<:saisies:option_groupe_utilisation:>'
    saisies:
      -
      <tableau de saisies pour les options concernant l'utilisation de la saisie (readonly, désactivation, etc.)>
  -
    saisie: 'fieldset'
    options:
      nom: 'affichage'
      label: '<:saisies:option_groupe_affichage:>'
    saisies:
      <tableau de saisies pour les options concernant l'affichage de la saisie message d'avertissement, etc.)>
  -
    saisie: 'fieldset'
    options:
      nom: 'conditions'
      label: '<:saisies:option_groupe_conditions:>'
    saisies:
      <tableau de saisies pour les conditions d'affichage de la saisie (afficher_si)>
  -
    saisie: 'fieldset'
    options:
      nom: 'validation'
      label: '<:saisies:option_groupe_validation:>'
    saisies:
      <tableau de saisies pour les options concernant la validation de la saisie (obligation, message d'erreur d'obligation, message relatif à l'obligation, etc..)>

À noter que certaines options seront automatiquement insérées par les formulaires de constructions de formulaire, si pertinent :

  • choix du nom dans le fieldset description
  • option de vérifications dans le fieldset validation ;
  • le plugin Interface pour Champs Extra s’occupe automatiquement d’ajouter son fieldset technique.

Il ne faut donc pas insérer ces options dans le constructeur .yaml.

On peut par ailleurs mutualiser le code, pour des options que l’on retrouve sur plusieurs saisies. C’est le cas par exemple pour les options concernant l’obligation et les afficher_si. Il suffit pour ce faire d’utiliser la syntaxe suivante :

      -  'inclure:<fichier yaml à inclure>'

Ainsi, les saisies livrés avec le plugin contiennent les lignes suivantes

  -
    saisie: 'fieldset'
    options:
      nom: 'conditions'
      label: '<:saisies:option_groupe_conditions:>'
    saisies:
      -  'inclure:saisies/_base/afficher_si.yaml'
  -
    saisie: 'fieldset'
    options:
      nom: 'validation'
      label: '<:saisies:option_groupe_validation:>'
    saisies:
      -  'inclure:saisies/_base/obligatoire.yaml'

Les informations spécifiques aux dev

Ainsi qu’expliqué, le constructeur d’une saisie sert à deux choses :

  • permettre de configurer la saisie via une interface graphique ;
  • documenter les options de la saisie.

Cependant, comment documenter des options qui, en raison de leur caractère technique et/ou pour des raisons de sécurité ne sont disponibles que pour les dev de squelettes/plugins, déclarant des saisies en SPIP/PHP, mais ne sont pas disponibles en interface graphique ?

La solution est simple : le constructeur de saisies peut contenir un tableau supplémentaire options_dev décrivant les options disponibles uniquement pour les dev.

Ainsi le constructeur de la saisie fieldset contient le tableau suivant :

options_dev:
      - 'inclure:saisies/_base/options_dev.yaml'
      -
        saisie: 'input'
        options:
          nom: 'icone'
          label: '<:saisies:option_icone_label:>'
          explication: '<:saisies:option_icone_explication:>'
      -
        saisie: 'input'
        options:
          nom: 'taille_icone'
          label: '<:saisies:option_taille_icone_label:>'

On documente ainsi :

  • les options de dev commune à l’ensemble des saisies (- 'inclure:saisies/_base/options_dev.yaml') ;
  • les options de dev spécifique à la saisie fieldset.

Il peut par ailleurs avoir certaines options où il faut fournir deux explications différentes :

  • une pour l’interface graphique ;
  • une pour les devs.

La première explication se trouve dans la clé explication, la seconde à la clé explication_dev.

C’est le cas cas, par exemple, pour décrire les data d’une saisie de type radio :

      -
        saisie: 'textarea'
        options:
          nom: 'datas'
          label: '<:saisies:option_datas_label:>'
          explication: '<:saisies:option_datas_sous_groupe_explication:>'
          explication_dev: '<:saisies:option_datas_sous_groupe_explication_dev:>'
          rows: 10
          cols: 50
        verifier:
          type: 'saisies_option_data'
      -
        saisie: 'case'

Tableau de réglages par défaut du constructeur

En plus du tableau d’options, le constructeur possède un tableau, défini par la clé defaut, indiquant les valeurs par défaut :
-  des options définies dans le constructeur ;
-  des types SQL pour le plugin Interface champs extras, qui n’est qu’une option parmi d’autres ;
-  de la vérification à effectuer pour cette saisie.

Exemple

Voici la description des options par défaut pour la saisie email, dans saisies/email.yaml :

defaut:
  options:
    label: '<:saisies:saisie_email_titre:>'
    size: 40
    # champs extras (definition du champ sql)
    sql: "text DEFAULT '' NOT NULL"
  verifier:
    type: 'email'

Fonctions avancées

Saisies autonomes

Définition

Ainsi que nous l’avions indiqué, la plupart des saisies utilisent pour leur constructeur le squelette saisies/_base.html et pour leur vue le squelette saisies-vues/_base.html.

Ces squelettes incluent les éléments que l’on retrouve dans la plupart des saisies :
-  conteneur HTML global ;
-  label ;
-  éventuels messages d’avertissement ou d’erreur ;
-  etc.

Toutefois, certaines saisies peuvent être dites « autonomes » et ne pas utiliser les éléments englobants de saisies/_base.html.
Parmi les saisies livrées avec le plugin, c’est le cas des saisies :
-  fieldset ;
-  hidden ;
-  destinataires ;
-  explication.

Il est possible d’utiliser le pipeline saisies_autonomes pour ajouter de nouvelles saisies autonomes.

Lorsque l’on créé une saisies autonomes, il faudra veiller :

  • à utiliser un conteneur adapté à la version de SPIP que l’on utilise :
    • li jusqu’à la version 3.0 incluse ;
    • div à partir de la version 3.1.
    • on pourra utiliser le bout de squelette suivant pour adapter automatiquement : <[(#ENV{saisies_base_conteneur,#DIV|sinon{li}})] ;
  • à passer correctement les différentes classes ;
  • à intégrer les informations nécessaires au bon fonctionnement de l’affichage conditionnel en mettant dans les attributs du conteneur :
    • [ data-id="(#ENV{id_saisie})"] ;
    • [ data-afficher_si="(#ENV*{afficher_si}|saisies_afficher_si_js{#ENV{_saisies}})"] ;

Exemples

Le plugin Gis déclare la saisie carte via le pipeline saisies_autonomes :

function gis_saisies_autonomes($flux) {
	$flux[] = 'carte';
	return $flux;
}

Voici le contrôleur de la saisie fieldset, dans saisies/fieldset.html :

#SET{pliable,#ENV{pliable}|et{#ENV{pliable}|!={non}}|?{'pliable', ''}}
#SET{plie,#ENV{plie}|et{#ENV{plie}|!={non}}|?{'plie', ''}}

[(#REM) S'il y a des erreurs pour au moins un des champs internes, on ne plie pas ! ]
#SET{champs_internes, #ENV{saisies}|saisies_lister_par_nom}
#SET{erreurs, #ENV**{erreurs}|sinon{#ARRAY}}
#SET{erreurs_fieldset, #GET{erreurs}|array_intersect_key{#GET{champs_internes}}}
[(#GET{erreurs_fieldset}|oui)
	#SET{plie, ''}
]
#SET{erreur_ici,#ENV**{erreurs/#ENV{nom}}}

<[(#ENV{saisies_base_conteneur,#DIV|sinon{li}})] class="fieldset[ fieldset_(#ENV{nom}|saisie_nom2classe)][ (#ENV{conteneur_class,#ENV{li_class}})][ (#ENV{type_saisie}|saisie_type2classe)][ (#GET{pliable})[ (#GET{plie})]]"[ data-id="(#ENV{id_saisie})"][ data-afficher_si="(#ENV*{afficher_si}|saisies_afficher_si_js{#ENV{_saisies}})"]>
	#ENV*{inserer_debut}
	<fieldset>

		[(#ENV{label}|oui)
			[(#REM) Récupérer le tag qui sera utilisé pour la légende ]
			[(#SET{tag, [(#ENV*{tagfield,<legend>}|inserer_attribut{class,legend})]})]
			[(#INCLURE{fond=inclure/fieldset_legend, env}|wrap{#GET{tag}})]
		]

		[<span class='erreur_message'>(#GET{erreur_ici})</span>]
		[<p class='explication'>(#ENV*{explication})</p>]
		[<em class='attention'>(#ENV*{attention})</em>]
		[(#ENV{saisies}|is_array|oui)
		<[(#ENV{saisies_base_conteneur,#DIV|sinon{ul}})] class="editer-groupe">
			#INCLURE{fond=#ENV{fond_generer,"inclure/generer_saisies"}, env, saisies=#ENV{saisies}, from_fieldset='on'}
		</[(#ENV{saisies_base_conteneur,#DIV|sinon{ul}})]>
		]
	</fieldset>
	#ENV*{inserer_fin}
</[(#ENV{saisies_base_conteneur,#DIV|sinon{li}})]>

et voici sa vue, dans saisies-vues/fieldeset.html :

[(#ENV{valeur_uniquement}|et{#ENV{valeur_uniquement}|!={non}}|non)
[<h3 class="legend[ (#ENV{vue_class})]">(#ENV{label})</h3>]
]

[(#ENV{saisies}|is_array|oui)
	#INCLURE{fond=inclure/voir_saisies, env, from_fieldset='on'}
]

Ajouter de nouvelles catégories — pipeline saisies_lister_categories

Le pipeline saisies_lister_categories permet d’ajouter (ou le cas échéant, de modifier ou supprimer) une ou plusieurs catégories de saisie.

Par exemple l’association Planète Sciences ajoute une catégorie pour les saisies propres à ses activités, avant toutes les autres catégories

/**
 * Ajout d'une catégorie de saisies plasci
 * @param array $flux
 * @return $flux modifié
**/
function plasci_saisies_lister_categories($flux) {
	$plasci = array(
		'plasci' => array(
			'nom' => _T('plasci:plasci')
		)
	);
	$flux = array_merge($plasci, $flux);//Mettre plasci en premier
	return $flux;
}

Nous avons ajouté une catégorie dont la clé est plasci et dont le nom humain est _T('plasci:plasci'). Pour l’instant, les catégories n’ont comme seule propriété que leur nom, mais cela pourrait changer à l’avenir.

À noter qu’une sécurité est appliquée, afin de s’assurer que la catégorie defaut se trouve systématiquement à la fin.

Modifier la liste des saisies proposées — pipeline saisies_lister_disponibles

Il est parfois utile de modifier la liste des saisies proposées, soit pour réaffecter des catégories, soit pour supprimer certaines saisies. Pour ce faire, on peut utiliser le pipeline saisies_lister_disponibles.

Par exemple, pour alléger son interface, Planète Sciences affecte la saisie evenements du plugin agenda à la catégorie plasci, et supprime toutes les saisies de catégorie objet.

/**
 * Met la saisie evenements dans la catégorie plasci
 * Supprime toute les saisies objets
 * @param array $flux
 * @return $flux
**/
function plasci_saisies_lister_disponibles($flux) {
	foreach ($flux as $saisie => &$description) {
		if ($saisie == 'evenements') {
			$description['categorie']['type'] = 'plasci';
			$description['categorie']['rang'] = -10;
		} elseif (isset($description['categorie']['type']) and $description['categorie']['type'] == 'objet') {
			unset($flux[$saisie]);
		}
	}
	return $flux;
}

Modifier les vérifications associables aux saisies — pipeline saisies_verifier_lister_disponibles

Lors de la configuration d’une saisie dans un constructeur de formulaire, il est possible de configurer une ou plusieurs vérifications, grâce au plugin verifier.
Toutefois, certaines vérifications ne s’appliquent pas, d’un point de vue logique, à certaines saisies. De plus, il peut être pertinent de dire qu’une saisie nécessite automatiquement certaines vérifications.

Par exemple la saisie fichiers du plugin cvtupload ne peut obtenir qu’une vérification fichiers, fournie par le plugin cvtupload. Il est du reste obligatoire d’utiliser cette vérification. Et enfin, cette vérification fichiers n’est possible que pour cette saisie fichiers.

C’est là qu’intervient le pipeline saisies_verifier_lister_disponibles. Elle reçoit en paramètre args la saisie, et renvoie en data la liste des vérifications disponibles et la liste des vérifications obligatoire. Voici le code du pipeline dans cvtupload.

/**
 * La saisie fichiers ne peut utiliser que la vérification fichiers
 * et elle doit obligatoirement l'utiliser
 * @param array $flux
 * @return $flux
 **/
function cvtupload_saisies_verifier_lister_disponibles(array $flux): array {
	$args = $flux['args'];
	$data = &$flux['data'];

	if ($args['saisie'] === 'fichiers') {
		$data['disponibles'] = [
			'fichiers' => $data['disponibles']['fichiers']
		];
		$data['obligatoires'] = ['fichiers'];
	} else {
		unset($data['disponibles']['fichiers']);
	}
	return $flux;
}

Héritage de saisies

Définition

Il est possible de créer des saisies « héritières » d’une autre saisie. Une telle saisie héritière est simplement une variante de la saisie « mère », avec simplement quelques réglages différents.

Par exemple la saisie choix_couleur est un simple héritage de la saisie radio.

Il est même possible de procéder à des héritages successifs.

L’intérêt du mécanisme d’héritage est d’éviter de répéter les options de la saisie mère dans le constructeur .yaml de la saisie héritière. On assure ainsi une plus grande cohérence dans les options proposées.

Contrôleur et vue de la saisie héritière

Un contrôleur et une vue étant un squelette, on pourra utiliser <INCLURE> ou #INCLURE pour inclure le contrôleur/la vue de la saisie mère.

Deux possibilités s’offrent à nous

  • Soit le balisage englobant est spécifique à la saisie héritée, comme pour les saisies radio/checkbox/case [2]. Dans ce cas la saisie héritière doit être déclarée comme autonome. L’inclusion sera de la forme <INCLURE{fond=saisies/_base,env,type_saisie=<nom de la saisie héritée>,<paramètres predefini par la saisie héritière...>} /> (pour la saisie). On prêtera attention au fait que le type de saisie doit être précisé après le passage de env.
  • Soit le balisage englobant est standard (si l’on hérite par exemple d’une saisie input). Dans ce cas, la saisie héritière n’est pas autonome. L’inclusion sera de la forme <INCLURE{fond=saisies/<nom de la saisie héritée>,env,<paramètres predefini par la saisie héritière...>} /> (pour la saisie).

À noter que pour la vue de la saisie, la notion d’autonomie n’a pas de pertinence. On appelera toujours <INCLURE{fond=saisies-vues/<nom de la saisie héritée>,env,<paramètres predefini par la saisie héritière...>} />.

Constructeur de la saisie héritière

Contrairement au constructeur de la saisie mère, le constructeur de la saisie héritière ne contient pas de tableau d’options, puisque le but est précisément de les hériter de la saisie mère.

Le constructeur de la saisie héritière possède une entrée heritage. Il s’agit d’un tableau avec les sous entrée suivante :

  • parent, indiquant la saisie dont on hérite les options ;
  • enlever_options ;
  • modifier_options ;
  • ajouter_options.
Options à enlever

La sous-entrée enlever_options permet d’enlever des options, listées sous forme de tableau.

heritage:
  parent: '<saisie_parente>'
  enlever_options:
    - 'placeholder'
    - 'type'
Options à modifier

La sous entrée modifier_options permet de modifier des options. Celles-ci sont listées sous forme de tableau. Chaque option est elle-même une saisie. Elle peut cependant contenir deux attributs supplémentaires :

  • nouveau_type_saisie pour modifier le type de saisie ;
  • mode :
    • si absent ou différent de 'fusionner', la nouvelle option remplace intégralement l’ancienne ;
    • si présent et égal à 'fusionner' :
      • les propriétés de la nouvelle option viennent remplacer celles de l’ancienne si elles portent le même nom ;
      • les propriétés de l’ancienne option sont conservées si elles ne sont pas redéfinies dans la nouvelle ;
      • les propriétés de la nouvelle option sont ajoutées si elles ne sont pas présentes dans l’ancienne
Options à ajouter

L’entrée ajouter_options permet d’ajouter des options. Il s’agit d’un tableau décrivant les options à ajouter sous formes de liste de saisies avec leur paramètres (puisque, rappelez-vous, les options d’une saisie sont elle même décrites sous forme de saisies !). Outre les paramètres standards pour décrire une saisie au sein d’un formulaire (type, options, etc), chaque saisie peut avoir, au choix, l’un des paramètres suivants, pour indiquer où insérer la saisie :

  • inserer_apres, pour indiquer après quelle saisie insérer notre nouvelle option/saisie ;
  • inserer_avant, pour indiquer avant quelle saisie insérer notre nouvelle option/saisie ;
  • chemin :
    • Si entre crochets, la saisie sera insérée à la fin du fieldset entre crochets. Exemple chemin: '[description]', pour insérer à la fin du fieldset description.
    • Si entre crochets suivis d’un entier entre crochets, la saisie sera insérée à une position précise dans un fieldset. Exemple : Exemple chemin: '[description][0]', pour insérer au début fin du fieldset description.
    • Si pas de crochet, pour insérer avant une saisie. Exemple chemin: 'label', pour insérer avant la saisie label.
Valeurs par défaut

Dans le constructeur d’une saisie héritière :

  • les valeurs de la propriété defaut remplacent celle de la saisie mère ;
  • si une propriété defaut est présente uniquement dans la saisie mère, elle est conservée ;
  • si une propriété defaut est présente uniquement dans la saisie héritière, elle est ajoutée.

Exemple

Voici le constructeur de la saisie choix_couleur définie dans saisies/choix_couleur.yaml :

titre: '<:saisie_choix_couleur:choix_couleur_titre:>'
description: '<:saisie_choix_couleur:choix_couleur_explication:>'
icone: 'images/saisies_radio.png'
categorie:
  type: 'choix'
  rang: 10000
heritage:
  parent: 'radio'
  modifier_options:
    -
      chemin: 'datas'
      mode: 'fusionner'
      options:
        explication: '<:saisie_choix_couleur:choix_couleur_datas_explication:>'
  ajouter_options:
    -
      saisie: 'fieldset'
      inserer_apres: 'validation'
      options:
        nom: 'analyse'
        label: '<:formidable:reponses_analyse:>'
      saisies:
        -
          saisie: 'case'
          options:
            nom: 'analyse_moyenne_temperature'
            label: '<:saisie_choix_couleur:analyse_moyenne_temperature_label:>'
            label_case: '<:saisie_choix_couleur:analyse_moyenne_temperature_label_case:>'
        -
          saisie: 'textarea'
          options:
            nom: 'temperature_corps_noir'
            label: '<:saisie_choix_couleur:temperature_corps_noir_label:>'
            explication: '<:saisie_choix_couleur:temperature_corps_noir_explication:>'
            defaut: ''
            afficher_si: '@analyse_moyenne_temperature@'
          verifier:
            type: 'saisies_option_data'
defaut:
  options:
    label: '<:saisie_choix_couleur:choix_couleur_titre:>'
    datas:
      choix1: '<:saisie_choix_couleur:choix_couleur_defaut_choix1:>'
      choix2: '<:saisie_choix_couleur:choix_couleur_defaut_choix2:>'
      choix3: '<:saisie_choix_couleur:choix_couleur_defaut_choix3:>'
    # champs extras (definition du champ sql)
    sql: "text DEFAULT '' NOT NULL"

Voici son contrôleur, dans saisies/choix_couleur.html :

[(#SET{data,[(#ENV*{data,#ENV*{datas}}|saisies_choix_couleur_preparer_data)]})]
<INCLURE{fond=saisies/_base,env,type_saisie=radio,data=#GET{data}} />

On le voit : on appelle simplement saisies/_base, en précisant de faire une saisie radio, avec le paramètre data construit spécifiquement pour la saisie choix_couleur. La saisie choix_couleur est déclarée comme saisie autonome.

et voici sa vue dans saisies-vues/choix_couleur.html :

[(#SET{data,[(#ENV*{data,#ENV*{datas}}|saisies_choix_couleur_preparer_data)]})]
<INCLURE{fond=saisies-vues/radio,env,data=#GET{data}} />

Vérifier les valeurs acceptables

Besoin et emploi

Certaines saisies ont un nombre prédéterminé de valeurs qui peuvent être envoyées au serveur, par exemple :
-  les boutons radio ;
-  les listes déroulantes ;
-  les inputs désactivés ou en lecture seule.

La plupart du temps, nous pouvons faire confiance à l’internaute pour ne pas tenter d’envoyer une autre valeur que les valeurs proposées.

Cependant, dans certains cas il est nécessaire de s’assurer que la valeur envoyée est bien présente parmi les valeurs possibles.

Pour ce faire, il faut dans le tableau de description des saisies avoir une option verifier_valeurs_acceptables :

$mon_tableau = array(
  '0' => array(), // une saisie
  '1' => array(), // une autre saisie
  'options => array( // un tableau d'option, dont
     'verifier_valeurs_acceptables' => true
  )
);

Si une telle option est activée, la fonction saisies_verifier() vérifiera si la valeur postée pour une saisie déterminée correspond aux valeurs prédéterminées.

Pour ce faire, elle s’appuie sur une fonction <saisie>_verifier_valeurs_acceptables dans le fichier saisies/<saisie>.php.

Exemple

Par exemple pour la saisie selection, le fichier saisies/selection.php contient la fonction suivante :

/**
 * Vérifie que la valeur postée
 * correspond aux valeurs proposées lors de la config de valeur
 * @param string $valeur la valeur postée
 * @param array $description la description de la saisie
 * @return bool true si valeur ok, false sinon,
**/
function selection_valeurs_acceptables($valeur, $description) {
	$options = $description['options'];
	if ($valeur == '' and !isset($options['obligatoire'])) {
		return true;
	}
	if (saisies_saisie_est_gelee($description) and isset($options['defaut'])) {
		return $valeur == $options['defaut'];
	} else {
		$data = saisies_trouver_data($description, true);
		$data = saisies_aplatir_tableau($data);
		$data = array_keys($data);
		if (isset($options['disable_choix'])) {
			$disable_choix = explode(',', $options['disable_choix']);
			$data = array_diff($data, $disable_choix);
		}
		return (in_array($valeur ,$data));
	}
}

Cette fonction fait appel à plusieurs fonctions du plugin Saisies :

  • saisies_trouver_data(), qui trouve l’option data de la saisie, ou à défaut datas, et la transforme si nécessaire en tableau ;
  • saisies_aplatir_tableau() qui permet de supprimer les niveaux d’imbrication dans le tableau ;
  • saisies_saisie_est_gelee(), qui indique si une saisie est « gelée » c’est-à-dire :
    • soit avec une option readonly activée ;
    • soit avec les deux options suivantes simultanément activées :
      • disable ;
      • disable_avec_post.

À noter que la plupart du temps, on pourra faire appel à une fonction de vérification des valeurs acceptables déjà définie. Ainsi, la saisie radio vérifie de la même manière que la saisie selection les valeurs acceptables :

/**
 * Vérifie que la valeur postée
 * correspond aux valeurs proposées lors de la config de valeur
 * @param string $valeur la valeur postée
 * @param array $description la description de la saisie
 * @return bool true si valeur ok, false sinon,
**/
function radio_valeurs_acceptables($valeur, $description) {
	include_spip('saisies/selection');
	// Structurellement, une saisie radio ou une saisie select, c'est un choix parmi N options.
	// Donc la vérif des données postées est la même
	return selection_valeurs_acceptables($valeur, $description);
}

Indiquer si une saisie peut avoir des enfants

Problématique

Certaines saisies peuvent avoir des enfants. C’est le cas par exemple de la saisie fieldset et de la saisie conteneur_inline. Ces enfants sont stockés dans un tableau saisies à la racine de la saisie mère. Il existe une fonction saisies_saisie_est_avec_sous_saisies() qui dit si la saisie peut contenir des sous-saisies. Par défaut, on considère qu’une saisie ne peut pas avoir d’enfants. Cependant, pour les saisies qui peuvent avoir des enfants, qu’une instance de cette saisie en ait ou pas, il faut définir une fonction <saisie>_est_avec_sous_saisies() dans le fichier saisies/<saisie>.php.

Exemple

Dans le fichier saisies/fieldset.php, on trouve :

/**
 * Un fieldset, c'est une saisie contenante
 * @param array $saisie
 * @return bool true
**/
function fieldset_est_avec_sous_saisies(array $saisie): bool {
	// Type de retour à changer en true lorsqu'on sera dans version de PHP qui l'autorise
	return true;
}

Indiquer si une saisie peut avoir recevoir un label/titre

Problématique

La très grande majorité des saisies peuvent recevoir un label ou un titre. Quelques saisies ne peuvent pas, comme la saisie conteneur_inline. Dans ce cas, il faut le préciser via une fonction spécifique <saisie>_est_labelisable() dans le fichier saisies/<saisie>.php. Cela permet à la fonction saisies_saisie_est_labelisable() de renvoyer la bonne valeur.

Exemple

Dans le fichier saisies/conteneur_inline.php, on trouve :

/**
 * Un conteneur_inline, ca n'a pas de label
 * @param array $saisie
 * @return bool false
**/
function conteneur_inline_est_labelisable(array $saisie): bool {
	// Type de retour à changer en false lorsqu'on sera dans version de PHP qui l'autorise
	return false;
}

Retourner le label d’une saisie

Problématique

Par défaut, le label d’une saisie correspond à l’option label(!). Mais certaines saisies peuvent être plus complexes, par exemple la saisie case, qui peut avoir à la place de label un label_case.

Pour pouvoir afficher correctement le label de la saisie lorsqu’on fait des résumés des saisies, on passe par la fonction saisies_saisie_get_label(). Cette fonction renvoie par défaut la valeur de l’option label de la saisie sauf si une fonction <saisie>_get_label() est disponible dans le fichier saisies/<saisie>.php

Exemple

Le fichier saisies/case.php contient le code suivant.

/**
 * Retourne le label de la saisie case
 * Par ordre de priorité le label_case
 * sinon le label
 * @param array $saisie
 * @return string
**/
function case_get_label(array $saisie): string {
	$label_case = $saisie['options']['label_case'] ?? '';
	if ($label_case) {
		return $label_case;
	} else {
		return $saisie['options']['label'] ?? '';
	}
}

Indiquer si une saisie peut recevoir une valeur

Problématique

Par défaut, une saisie correspond à un champ de formulaire. Elle est donc susceptible de recevoir une valeur lors de l’envoi du formulaire. Certaines saisies ne le peuvent pas, car elles se contentent de regrouper des saisies : fieldset, conteneur_inline par exemple.

Dans ce cas il faut définir une fonction <saisie>_est_champ() dans le fichier saisies/<saisie>.php pour que la fonction globale saisies_saisie_est_champ() renvoie la bonne valeur.

Exemple

Le fichier saisies/conteneur_inline.php contient la fonction suivante.

/**
 * Un conteneur_inline, ca n'est pas un champ
 * @param array $saisie
 * @return bool false
**/
function conteneur_inline_est_champ(array $saisie): bool {
	// Type de retour à changer en false lorsqu'on sera dans version de PHP qui l'autorise
	return false;
}

Des pipelines et outils pour les affichages conditionnels

Les affichages conditionnels permettent d’afficher une saisie en fonction de la valeur d’une autre saisie. Pour ce faire, le plugin Saisies génère dynamiquement du code Javascript et PHP.

La plupart du temps, le plugin Saisies est capable de déterminer automatiquement le code à générer. Il peut cependant arriver qu’il faille l’aider grâce à des pipelines.

Saisies tabulaires — pipeline saisie_est_tabulaire

Définition

La plupart des Saisies renvoient un champ unitaire. Toutefois, certaines saisies renvoient un tableau de données. Tel est le cas :
-  de la saisie checkbox, qui renvoie un tableau des cases cochées ;
-  de la saisie evenements du plugin Agenda, qui selon la configuration peut renvoyer un champ unitaire (lorsque le choix est sous forme de boutons radio ou de liste déroulante) ou un tableau de données (lorsque le choix est sous forme de cases à cocher).

Le pipeline saisie_est_tabulaire doit renvoyer le paramètre data à true si la saisie est tabulaire. Ceci permet d’effectuer de manière correcte les vérifications côté PHP.

Exemple

Voici comment le plugin Agenda utilise ce pipeline pour la saisie evenements.

/**
 * Pour la saisie de type événement, indique si les données renvoyées sont tabulaires ou pas
 * @param $flux
 * @return $flux
**/
function agenda_saisie_est_tabulaire($flux) {
	$args = $flux['args'];
	if ($args['saisie'] != 'evenements') {
		return $flux;
	}
	if ($args['options']['type_choix'] == 'checkbox') {
		$flux['data'] = true;
	}
	return $flux;
}

Pseudo-saisies — Pipeline saisies_afficher_si_saisies

Définition

Normalement une saisie correspond à un seul champ de formulaire HTML. Il peut cependant arriver que certaines saisies proposent plusieurs champs.

Par exemple, la saisie evenements du plugin agenda possède un champ <nom_de_la_saisie>_liste_attente qui est automatiquement mis à on si l’internaute sélectionne un évènement pour lequel il ne reste plus de place. Cela peut être utile pour gérer l’inscription à des évènements avec des listes d’attente.

Pour que le test conditionnel sur @<nom_de_la_saisie>_liste_attente@ soit possible, il est nécessaire que le javascript généré connaisse l’existence de ce champ html <nom_de_la_saisie>_liste_attente.

Pour ce faire, nous devons utiliser le pipeline saisies_afficher_si_saisies. Celui-ci reçoit un tableau de saisies, et doit renvoyer le même tableau, complété, le cas échéant, de « pseudo-saisies » correspondant aux champs HTML non homonymes aux saisies.

Exemple

Voici comment le plugin Agenda utilise ce pipeline pour gérer le champ <nom_saisie_evenements>_liste_attente :

/**
 * Si on a une saisie de type événement, ajoute, si nécessaire, au tableau de déclaration de saisies la saisie _liste_attente correspondante.
 * Utile pour les afficher_si
 * @param $flux les saisies
 * @return $flux les saisies modifier
**/
function agenda_saisies_afficher_si_saisies($flux) {
	//Ne pas refaire 36 fois le calcul
	static $old;
	static $new;
	if ($old == $flux) {
		return $new;
	}
	$old = $flux;
	$new = $flux;
	include_spip('inc/saisies');
	$saisies_evenements = saisies_lister_avec_type($flux, 'evenements');
	foreach ($saisies_evenements as $saisie => $description) {
		if (isset($description['options']['liste_attente']) and $description['options']['liste_attente']) {
			$saisie_inserer = array(
				'saisie' => 'evenements_liste_attente',
				'options' => array(
					'nom' => $description['options']['nom'].'_liste_attente',
					'label' => $description['options']['label'].' ('.strtolower(_T('saisie_evenements:liste_attente_label')).')',
					'afficher_si' => 'false',
				)
			);
			$new = saisies_inserer_apres($new, $saisie_inserer, $description['options']['nom']);
		}
	}
	return $new;
}

On remarquera l’emploi du test sur une variable statique. Cet emploi est nécessaire pour des raisons de performances, car le pipeline est appelé à chaque fois qu’une saisie possède un affichage conditionnel.

Créer les tests d’afficher_si spécifiques à certains types de saisie

Cette partie est très technique. Rares sont les personnes qui auront besoin de s’en servir, car ce qui est livré avec le plugin saisies fonctionne dans 99,99% des usages.

Problématique

Il est aisé côté PHP de vérifier qu’un test d’afficher_si est bien valide. En effet, les données que reçoit PHP lors d’une requête se retrouvent forcément dans _$_POST ou $_FILES. C’est pourquoi, pour l’heure, le plugin ne prévoit pas que l’on puisse ajouter des réglages spécifiques à certains types de saisies.

Côte Javascript, les structures de données sont parfois plus complexe. Depuis la version 3.50.0 du plugin, la détection des tests javascript à effectuer marchent dans 99% des cas. Chaque test individuel est remplacé par un appel à la fonction JavaScript saisies_afficher_si() qui reçoit en argument un tableau décrivant les composants de ce texte (champ à tester, valeur à tester, opérateur, etc.).

Pour certains type de saisies toutefois, on peut imaginer de brancher sur un autre tests Javascript. Il faut pour cela créer une fonction saisies_afficher_si_js_<type_de_saisie>() dans saisies_afficher_si_js/<type_de_saisie>.php. Cette fonction reçoit en entrée l’analyse syntaxique formelle du test, sous forme de tableau, et retourne en sortie une code évaluable par Javascript

Exemple

C’est par exemple le cas de la saisies fichiers du plugin CVT-Upload, puisqu’il s’agit de tester combien de fichiers on s’apprête à envoyer.

Pour ce faire, le plugin définit dans le fichier saisies_afficher_si_js/fichiers.php une fonction saisies_afficher_si_js_fichiers.php. Celle-ci génère un code JS capable d’évaluer le test portant sur une saisies fichiers en fonction de la structure des objets JS pour les input de type file.

Lien avec Champs Extras

Le plugin Champs Extras propose des affichages par défaut pour les différentes saisies, ainsi que des modalités par défaut de serialisation en base pour les saisies qui renvoient plusieurs valeurs.

Il est toutefois possible de proposer des exceptions pour certaines saisies.

#LISTER_VALEURS

Pour rappel, #LISTER_VALEURS permet de lister les valeurs d’un champ extra.

Il est possible de créer une fonction champs_extras_calculer_balise_LISTER_VALEURS_<type_de_saisie>() pour définir un retour différent. La fonction est à placer dans un fichier champs_extras/calculer_balise_lister_valeurs_<type_de_saisie>.php. Elle est appelée via charger_fonction.

Exemple

Le plugin Saisies propose dans champs_extras/calculer_balise_lister_valeurs_choix_grille.php la fonction suivante :

/**
 * Déclaration de la balise LISTER_VALEURS pour la saisie <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+Y2hvaXhfZ3JpbGxlPC9jb2RlPg=="></span>
 * @param string $objet
 *     Type d'objet
 * @param string $colonne
 *     Nom de la colonne SQL
 * @param string $cles
 *     Valeurs enregistrées pour ce champ dans la bdd pour l'objet en cours
 * @return array|string vide
 *		Tableau de type 'Clé de ligne|Valeur de ligne' => 'Ligne en valeur humaine|Valeur de ligne en humain'.
 * Par ex 'Ligne1|Colonne1' => 'Ma première ligne|Ma première colonne'
**/
function champs_extras_calculer_balise_LISTER_VALEURS_choix_grille($objet, $colonne, $cles) {
	if (
		$options = calculer_balise_CHAMP_EXTRA($objet, $colonne)
		and isset($options['data_rows'])
		and isset($options['data_cols'])
	) {
		$data_rows = saisies_chaine2tableau($options['data_rows']);
		$data_cols = saisies_chaine2tableau($options['data_cols']);
		$retour = [];
		$valeurs = saisies_chaine2tableau($cles);
		foreach ($valeurs as $cle => $valeur) {
			if (is_array($valeur)) {
				$colonne_humaine = join('|',
					array_map(
						function ($i) use ($data_cols) {
							return $data_cols[$i];
						},
						$valeur
					)
				);
				$retour["$cle|".join('|', $valeur)] = $data_rows[$cle].'|'.$colonne_humaine;
			} else {
				$retour["$cle|$valeur"] = $data_rows[$cle].'|'.$data_cols[$valeur];
			}
		}
		return $retour;
	} else {
		return '';
	}
}

Sérialisation

Pour une saisie renvoyant plusieurs valeurs (type tableau), il faut serialiser, c’est-à-dire transformer en chaîne de caractère, pour stocker en base. Par défaut champs extras serialise en séparant les valeurs par une virgule. Ce n’est toutefois pas toujours adapté.

Il est possible de définir dans un fichier champs_extras/serialiser_<type_de_saisie>.php une fonction champs_extras_serialiser_<type_de_saisie>() pour définir un autre mode de sérialisation.

Exemple

Le plugin saisies définit une sérialisation spécifique pour la saisie choix_grille. On trouve dans champs_extras/serialiser_choix_grille.php la fonction suivante :

/**
 * Sérialise les réponses à un champ extra de type <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+Y2hvaXhfZ3JpbGxlPC9jb2RlPg=="></span> pour encodage en base.
 * @param array $extra
 *     La valeur reçue en POST
 * @param array $saisie
 *     La description de la saisie
 * @return string
 *		 Forme serialisé, en l'occurence avec saisies_tableau2chaine
 **/
function champs_extras_serialiser_choix_grille($extra, $saisie) {
	include_spip('inc/saisies');
	return saisies_tableau2chaine($extra);
}

Saisies obsolètes

Définition

Il est possible de déclarer une saisie comme obsolète dans le constructeur .yaml.

Une saisie obsolète n’est plus proposée à la construction d’un formulaire. En revanche les saisies déjà présentes dans le formulaire continuent d’exister.

Pour indiquer une saisie comme obsolète, il suffit de mettre l’attribut obsolete à true dans le .yaml :

Exemple

Un exemple de saisie obsolète est la saisie oui_non, qu’il convient de remplacer par une saisie radio avec des intitulés explicites.

titre: '<:saisies:saisie_oui_non_titre:>'
description: '<:saisies:saisie_oui_non_explication:>'
icone: 'images/saisies_oui_non.png'
obsolete: true

Notes

[1La description des choix peut être passée dans #ENV{data}. #ENV{datas} est possible, mais déprécié.

[2À partir de saisie v4.0.0

Discussion

Aucune discussion

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom