Descript¶
Structure¶
La descript se présente comme un arbre XML et est constituée de différents types d'éléments. Elle a la structure suivante:
descript
+- models
| +- model
| | +- groups
| | | +- group
| | | | +- fields
| | | | | +- field
| | | | | +- properties
| | | | | | +- property
| | | | | +- policies
| | | | | +- policy
| | | | +- properties
| | | | | +- property
| | | | +- policies
| | | | +- policy
| | +- properties
| | | +-property
| | +- policies
| | +- policy
+- properties
| +- property
+- policies
+- policy
- L'élément racine s'appelle
<descript>
- Les éléments
<policies>
peuvent être placés au niveau de la descript, des modèles, des groupes ou des champs. Ils permettent de spécifier les règles de sécurités relatives à la lecture/écriture.
À noter que les policies définies au niveau du nœud<descript>
sont traitées comme celles définies au niveau du fichier de configuration de l'application. - Les éléments
<model>
décrivent les modèles de dossiers que gère l'application. Le modèle est la description de la structure d'un dossier. Vu autrement, le dossier est une instance de modèle. -
Les éléments
<group>
sont des regroupements de champs. Ces regroupements de champs sont appelés tuples. Donc le groupe est la description de la structure d'un tuple. Vu autrement, le tuple est une instance de groupe.Il existe 3 types de groupes:
- les groupes
single
constitués d'un seul tuple - les groupes
multi
qui peuvent avoir 0 ou n tuples (ces groupes permettent de décrire des tables de valeurs) - le groupe principal (maingroup) : il s'agit du groupe dont
l'identifiant (la clé primaire) fait également office d'identifiant de
dossier; dans les faits, le groupe principal est forcément un groupe de
type
single
Exception faite du groupe principal, les groupes
single
etmulti
sont structurellement assez similaires : ils sont constitués de tuples qui possèdent tous une clé primaire et qui référencent le groupe principal (pour permettre la liaison des tuples d'un même dossier). Du point de vue logique, la seule réelle différence vient du fait que le moteur crée automatiquement les tuplessingle
à la création du dossier et qu'il empêche l'ajout et la suppression de tuplessingle
. - les groupes
-
Les éléments
<field>
décrivent les champs de l'application - La hiérarchie des éléments doit être respectée (si un élément
<field>
est placé en-dehors d'un<group>
, il sera ignoré) - L'ordre des éléments joue un rôle pour la construction de l'arbre de sortie du moteur: les éléments (propriétés, modèles, groupes, fields) seront repris dans l'arbre dans le même ordre que celui de la descript.
- Les éléments
<properties>
peuvent être placés au niveau de la descript, des modèles, des groupes ou des champs. Par ailleurs, les propriétés peuvent contenir du xml: celui-ci sera repris au niveau de l'arbre de sortie. À noter toutefois qu'il est possible de demander à Ewt d'adapter certains éléments. Le chapitre relatif aux propriétés ci-dessous donne plus de détails.
Dans la suite de la documentation, nous reviendrons fréquemment sur les
notions de modèle, dossier, groupe, tuple, etc. La table ci-dessous illustre
la correspondance entre un terme utilisé au niveau de la description
(fichier descript.xml
) et le terme correspondant pour désigner une
instance. Par exemple, le dossier est une instance d'un modèle, le tuple
est une instance de groupe, etc.
Description | Instance |
---|---|
Modèle | Dossier |
Groupe | Tuple |
Champ | Valeur |
Modèle (<model>
)¶
L'élément <model>
permet de décrire un modèle. Un modèle est un ensemble
de champs structurés en groupes. Le moteur s'appuie sur la description des
modèles pour déterminer la représentation mémoire des données à traiter.
Chaque modèle reconnaît les éléments suivants:
-
attribut
name
: nom du groupe (nom de code interne à l'application) -
attribut
label
: facultatif, libellé décrivant le modèle. Cet attribut permet de spécifier le libellé à reprendre dans l'arbre de sortie dans le cas où aucun libellé d'internationalisation n'est trouvé dans le bundle associé à la descript. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
description
: facultatif, texte court décrivant le modèle. Cet attribut permet de spécifier le descriptif à reprendre dans l'arbre de sortie dans le cas où aucun libellé d'internationalisation n'est trouvé dans le bundle associé à la descript. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
maingroup
: nom du groupe principal, c.-à-d. le groupe de typesingle
dont le tuple sera le tuple principal du dossier. L'identifiant du dossier reprend ainsi l'identifiant de son tuple principal. -
attribut
statesmodel
: facultatif, nom du modèle d'états à appliquer pour les dossiers du modèle courant; l'attribut fait référence à un modèle d'état -
attribut
statefield
: facultatif, nom du champ dans lequel est enregistré le statut du dossier; le champ doit exister dans le groupe principal (maingroup
). -
attribut
autoflush
: facultatif, liste d'actions pour lesquelles on souhaite activer l'auto-flush. L'auto-flush est une fonctionnalité consistant à sauvegarder automatiquement les données d'un dossier en base de données lors de n'importe quelle action déclenchée depuis l'interface utilisateur. Ainsi, les données sont automatiquement sauvegardées à la fermeture d'un dossier, lors d'un ajout de tuple, etc. Par défaut l'auto-flush est activé pour toutes les actions. Il est cependant possible de spécifier explicitement les actions pour lesquelles on autorise l'auto-flush. Le cas échéant, les actions doivent être inscrites sous la forme d'une chaîne séparée par une virgule, par exemple "save,close" si on souhaite que le flush se fasse lors de l'enregistrement et lors de la fermeture d'un dossier. La liste des actions est disponible dans le chapitre qui traite des identity policies dans le document Gestion d'accès - Policies. À noter que l'action "save" est implicite : elle effectue toujours un flush (c'est son rôle) indépendamment de la valeur de l'attributautoflush
. -
attribut
locktype
: facultatif, type de lock à utiliser pour les dossiers du modèle. L'attribut peut prendre l'une des valeurs suivantes:optimistic
(en réalité, n'importe quelle valeur différente des autres valeurs ci-dessous): gestion optimiste de la concurrence. C'est le mode par défaut si l'attributlocktype
n'est pas spécifié.pessimistic
(en réalité, n'importe quelle valeur qui débute par le caractèrep
): gestion pessimiste de la concurrencenone
oufalse
: pas de gestion de la concurrence
Ces notions sont détaillées dans le chapitre Gestion de la concurrence.
-
attribut
locklevel
: facultatif, niveau de lock attendu pour les dossiers du modèle. L'attribut peut prendre l'une des valeurs suivantes:tuple
(ou toute autre valeur différente de celles ci-dessous): gestion de lock au niveau tuple. Dans ce cas, un conflit de concurrence détecté lors d'une mise à jour n'impacte que le tuple en question. Les autres tuples du dossier sont mis à jour lorsque cela est possible. Il s'agit du mode par défaut si l'attribut n'est pas spécifié.doc
oudocument
: gestion de lock au niveau document. Dans ce cas, un problème de concurrence détecté au niveau d'un des tuples du dossier entraîne le rollback de toutes les requêtes de mises à jour des tuples (aucun tuple n'est mis à jour)none
oufalse
: pas de test de concurrence. Écrasement de valeur existante ou mode "Client Wins" / "Last in Wins".
Ces notions sont détaillées dans le chapitre Gestion de la concurrence.
-
attribut
lockforce
: facultatif, flagtrue
/false
indiquant que le modèle autorise l'utilisateur à forcer un enregistrement de valeurs en base de données en cas de problèmes de concurrence détecté, en effectuant en secondsave
des valeurs; cela ne concerne que la gestion optimiste -
attribut
indexname
: facultatif, nom de l'index dans lequel les dossiers doivent être indexés -
attribut
indexstore
: facultatif, flagtrue
/false
indiquant que la valeur des champs doit être stockée ou non dans l'index (voir le chapitre Indexation et recherche pour comprendre ce que cela signifie) -
attribut
indexmode
: facultatif, mode d'indexation des valeurs (voir Indexation et recherche) -
attribut
policy
: facultatif, nom de la ou des policies à appliquer au modèle. Il est possible de référencer plusieurs policies en les séparant par une virgule. Les policies référencées via cet attribut s'ajoutent à celles définies ou référencées via l'élémentpolicies
-
élément
policies
: références ou descriptions de policies qui prévalent pour le modèle et les groupes qu'il contient -
élément
groups
: groupes de champs du modèle -
élément
views
: types de vues du modèle
Groupe (<group>
)¶
L'élément <group>
représente un ensemble de champs. Un groupe est
généralement lié à une table définie dans le fichier de description de la
base de données (voir fichier schema.xml
).
Il est important de voir qu'un groupe ne peut pas réunir des champs de plusieurs tables. Il est par contre possible de construire un groupe qui ne soit lié à aucune table.
Il existe 2 types de groupes:
- Les groupes single: un groupe single est un groupe dans lequel il ne peut exister qu'une seule instance de valeur par champ au niveau d'un dossier. Dans le cas d'un dossier "Personne", un champ single sera donc par exemple son nom ou sa date de naissance.
- Les groupes multi: un groupe multi est un groupe dans lequel il peut exister de 0 à n valeurs par champ au niveau d'un même dossier. Si on reprend l'exemple d'un dossier "Personne", on peut imaginer des groupes multi tels que "Adresses", "Enfants", "Comptes", etc. Dans chacun de ces groupes on pourra avoir entre 0 et n entrées.
Les attributs et éléments reconnus associés au groupe sont les suivants:
-
attribut
name
: nom du groupe, servant à identifier ce dernier -
attribut
type
: type de groupe; le groupe peut être de typesingle
oumulti
. Un groupesingle
contient un et un seul tuple alors qu'un groupemulti
peut en contenir 0 ou n.Chaque dossier doit avoir un groupe principal de type
single
, c'est-à-dire un groupe dont le tuple contient les données de base du dossier. L'identifiant du tuple fait de facto office d'identifiant du dossier. -
attribut
reffield
(voir également l'encadré plus bas): Les groupesmulti
ne pouvant pas être main group, ils doivent obligatoirement spécifier comment ils sont rattachés au main group. Pour cela, la définition du groupe devra spécifier un attributreffield
qui indique quel champ du groupemulti
contient la référence vers le main group.Les groupes
single
qui ne sont pas main group doivent également utiliser cet attribut pour spécifier le lien avec le groupe principal. -
attribut
table
: optionnel; table qui enregistre les données des tuples du groupe.La plupart du temps, on associera un groupe à une et une seule table de base de données. Celle-ci doit être déclarée dans le fichier de schéma (
schema.xml
). Il est toutefois possible de définir un groupe sans table, par exemple si on souhaite définir des champs qui sont uniquement des éléments d'interfaces (boutons, libellés statiques, etc.) ou des champs qui ont uniquement une valeur stockée en mémoire. -
attribut
mainfield
(voir également l'encadré plus bas): optionnel; champ du groupe faisant office d'identifiant au sein du dossier. Si non défini, le moteur se rabat sur le champ dont la colonne associée est la primary key (à condition que celle-ci ne repose que sur une seule colonne) -
attribut
label
: optionnel; libellé par défaut à utiliser pour désigner le groupe lorsque l'application ne fournit pas d'élément d'internationalisation pour ce dernier. Dit autrement, cet attribut permet de spécifier un libellé par défaut pour désigner le groupe au niveau de l'interface utilisateur. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
description
: optionnel; texte descriptif par défaut relatif au groupe. Il s'agit d'un libellé détaillant le rôle du groupe. À noter que ce libellé est automatiquement substitué par le moteur si un descriptif est disponible dans les bundles de langues de l'application. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
indexstore
: optionnel; catalogue d'index associé au groupe. Cet attribut remplit le même rôle que pour le niveaumodel
et vient surcharger ce dernier s'il est défini. -
attribut
indexmode
: optionnel; mode d'index associé au groupe. Cet attribut remplit le même rôle que pour le niveaumodel
et vient surcharger ce dernier s'il est défini. -
attribut
standalone
: optionnel; flag indiquant que les tuples peuvent exister de façon indépendante du dossier. Concrètement, ce flag permet d'indiquer que le tuple n'appartient pas exclusivement au dossier.Il joue un rôle important dans un cas bien précis que nous détaillons ici. Ewt permet de référencer des tuples de dossiers tiers et de les intégrer au sein de groupes multi. La documentation de la méthode
$msg.remapTargets
présente un cas d'utilisation. Pour résumer, prenons le cas d'un dossier "demande" qui possède un identifiantidDemande
et une référence de dossier clientidClient
. Depuis le dossier client, on peut reprendre le dossier de demande au sein d'un groupe multi simplement en liant la table single de la demande au niveau du groupe multi et en déclarantidDemande
commemainfield
etidClient
commereffield
.Cette construction est fonctionnelle, mais présente des effets de bord car le dossier client croit que les tuples "demandes" lui appartiennent. La méthode
$msg.remapTargets
explique l'effet de bord du point de vue de la gestion d'état. On peut mentionner un autre effet de bord plus problématique avec la suppression de dossier: en effet, le fait de supprimer le tuple de la demande. Cela peut amener à des dossiers incomplètement supprimés au niveau des demandes (en particulier si celle-ci contenait à son tour d'autres groupes).Le flag
standalone
, lorsqu'il est activé àtrue
, influe sur le comportement du moteur vis-à-vis des tuples du groupe. En particulier, le moteur ne cherchera pas à supprimer les tuples lors d'undelete
du dossier, il bloquera lesaddTuple
, lesdelTuple
ainsi que les travaux relatifs au groupe lors desclone
.Par défaut le flag est
false
. -
attribut
policy
: optionnel; peut être utilisé pour référencer une ou plusieurs policies à appliquer au groupe. Il est possible de référencer plusieurs policies en les séparant par une virgule. Les policies référencées via cet attribut s'ajoutent à celles définies ou référencées via l'élémentpolicies
. -
élément
policies
: références ou description de policies qui prévalent pour le groupe et les champs qu'ils contient -
élément
fields
: champs du groupe
primary key, reffield et mainfield
Définitions
Le moteur utilise les notions de primary key, de reffield et de mainfield. Afin de lever toute ambiguïté à leur sujet, nous allons ici les décrire brièvement.
La primary key (ou clé primaire) est une notion spécifique à la gestion
de la base de données. De ce fait, la notion appraît uniquement au
niveau du fichier de description de la base de données (schema.xml
).
La clé primaire est l'élément qui permet d'identifier de façon unique
une entrée dans une table. Elle peut être constituée d'une ou plusieurs
colonnes. En pratique, un groupe single
devrait toujours s'appuyer sur
une table possédant une clé primaire constituée d'une colonne unique. Un
groupe multi
quant à lui peut s'appuyer sur une table possédant une
clé primaire constituée d'une ou deux colonnes.
Le reffield est une notion logique qui est propre à la
description métier de l'application (descript.xml
). Cet attribut
permet d'indiquer quel champ fait le lien avec le groupe principal. Il
n'est donc pas nécessaire sur le groupe principal, mais uniquement sur
les autres groupes. Le reffield sera généralement associé à une
colonne qui, au niveau du schéma, possède un attribut fk
(ou FK
, ou
foreignReference
) pointant vers le mainfield du groupe principal.
Le mainfield est une notion logique qui est propre à la description
métier de l'application (descript.xml
). Le mainfield est le champ
qui identifie le tuple au sein de son groupe. Il ne s'agit pas
forcément d'un identifiant unique au sein de la table associée, mais
d'un identifiant qui permet de distinguer les tuples d'un même dossier.
Dans le cas du groupe principal, le mainfield prend également le rôle
d'identifiant de dossier.
On peut se représenter le rôle du mainfield en considérant les expressions de contextes utilisées dans les applications Ewt. Prenons l'exemple suivant:
vente[1000].article[12].prix
Cette expression désigne le prix de l'article 12 de la vente 1000. La valeur 1000 est la valeur du mainfield du groupe principal. La valeur 12 est la valeur du mainfield du groupe article.
Au sein d'un groupe multi
lié à une table possédant une clé primaire
constituée de deux colonnes, le couple reffield/mainfield devrait
être l'équivalent logique de la clé primaire de la table associée: le
champ reffield enregistrera le numéro de dossier alors que le
champ mainfield enregistrera le numéro de ligne au sein du dossier.
Mise en application
Dans une application Ewt, on peut avoir différents types de groupes. Nous passons ces types en revue à travers l'extrait de description ci-dessous. Afin de pouvoir nous focaliser sur l'objet du propos, nous avons volontairement simplifié la syntaxe de descript dans l'exemple ci-dessous:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
Tri, filtre et pagination¶
Les notions de tri, de filtre et de pagination sont regroupées sous le terme générique d'arrangement, qui prend en français le sens d'organisation, de disposition ou d'agencement. Un arrangement est donc une façon d'agencer les tuples à l'écran.
On définit une règle d'arrangement au niveau du groupe via l'élément
<arrangement>
. L'élément est composé de trois parties: sort
, filter
et
pagination
. Les types d'arrangement sort
et filter
s'appuient
exclusivement sur des champs du groupe. Chaque champ du groupe pourra à son
tour spécifier comment la valeur doit être intégrée dans la clause de tri ou
de filtre, ce qui permet de construire des clauses de tri et de filtre
avancées (voir chapitre Champ (<field>
)).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Tri (<sort>
)¶
Exemple de règles de tri:
1 2 3 4 |
|
L'attribut customizable="true"
indique que l'on autorise l'utilisateur à
modifier les règles de tri.
L'ordre des éléments joue un rôle important : le moteur conserve l'ordre des
éléments lorsqu'il construit la clause ORDER BY
.
En l'absence de règle d'arrangement de type sort
, le moteur applique
automatiquement une clause ORDER BY
basée sur l'identifiant de tuple afin
que l'ordre des tuples reste constant.
Filtre (<filter>
)¶
Une référence de champ permet d'indiquer sur quel champ doit porter le filtre. Comme le filtre est lui-même susceptible de contenir une liste de valeurs et/ou une valeur par défaut, on peut considérer la référence de champ comme un champ à part entière. Ainsi, certaines propriétés d'un champ de filtre utilisent la même syntaxe que les champs de données eux-même, en particulier pour la définition d'options et de valeur par défaut.
Exemple de règles de filtre:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
L'attribut customizable="true"
indique que l'on autorise l'utilisateur à
modifier les filtres de colonnes.
Les attributs name
et method
sont obligatoires pour chaque référence de
champ.
L'attribut name
indique le nom du champ sur lequel porte le filtre. Il
doit correspondre à un champ du groupe.
L'attribut method
permet d'indiquer la méthode de filtre à appliquer. Il
peut prendre les valeurs suivantes:
exact
: Cette méthode filtre en appliquant un test d'égalité stricte.contains
: Cette méthode applique un filtre de typelike
-
range
: Cette méthode filtre de manière différente en fonction du nombre de paramètres qu'on lui passe:- Lorsque 2 paramètres sont spécifiés, le filtre effectue un test
between
sur les deux valeurs : seules les valeurs situées entre les bornes sont retenues. - Lorsque seul le premier paramètre est défini, le filtre effectue un test
de type
>=
. - Lorsque seul le second paramètre est renseigné, le filtre effectue un
test de type
<=
.
- Lorsque 2 paramètres sont spécifiés, le filtre effectue un test
-
list
: Ce type applique un filtre de typein
.
Il est possible de spécifier que l'on souhaite appliquer une logique
inversée. Ainsi, la présence de l'attribut inverse="true"
fait fonctionner
les méthodes de filtre en mode inverse:
- inverse
exact
: Revient à faire un test!=
- inverse
contains
: Revient à faire un test de typenot like
- inverse
range
: Revient à faire un testnot between
,<
ou>
en fonction du nombre et des valeurs de paramètres - inverse
list
: Revient à faire un testnot in
.
Les règles de filtre font toujours référence à un champ (référence faite par
l'attribut name
). La valeur de référence quant à elle peut être définie
sous forme de texte (mode="text"
), au moyen d'une requête SQL
(mode="sql"
) ou au moyen d'un script (mode="script"
). Dans les modes
sql
et script
, le moteur se charge de l'évaluation afin de déterminer la
valeur à utiliser comme condition.
L'attribut type
joue le même rôle que dans le cas des champs (voir
Champ (<field>
)) : il permet d'indiquer le type de champ
que l'interface doit afficher. Si la valeur de l'attribut est auto
(ou si
l'attribut n'est pas spécifié), alors le type reprend le type du champ
référencé.
L'attribut class
permet d'indiquer la classe de filtre. Il existe deux
classes, et un filtre peut appartenir à l'une, l'autre ou les deux:
-
variable
: Cette classe indique que le champ fait partie des éléments personnalisables par l'utilisateur. La classevariable
est active par défaut lorsque l'attributclass
n'est pas spécifié.
Le moteur reprendra dans l'arbre de sortie uniquement les filtres variables. -
permanent
: Cette classe indique que le filtre est permanent, c'est-à-dire qu'il est appliqué en permanence et n'est pas modifiable par l'utilisateur. On indiquera un filtre permanent par exemple lorsque l'on souhaite qu'un groupe ne soit constitués que d'un sous-ensemble de tuples d'une table. Le filtre en question peut par exemple tenir compte des rôles de l'utilisateur connecté: ainsi le groupe n'affichera que des tuples que l'utilisateur est autorisé de voir et/ou modifier.
Les filtres purement permanents ne sont pas repris dans l'arbre de sortie du moteur.
Il est possible de spécifier class="both"
ou class="variable,permanent"
(les deux notations sont équivalentes) pour indiquer que le filtre
appartient aux deux classes.
Remarque sur la notation
L'utilisation d'une classe combinée (avec both
ou variable,permanent
)
permet de simplifier la notation. Précisons cependant qu'il est également
possible de faire coexister les filtres permanent et variable au sein de
références de champs distinctes.
L'exemple donné plus haut utilise la classe both
pour le champ de
filtre position
. On aurait également pu construire le filtre ainsi, ce
qui donne le même résultat:
1 2 3 4 5 6 7 |
|
Il peut très bien arriver que le filtre permanent et le filtre variable
n'utilisent pas la même méthode. On peut par exemple avoir un filtre
permanent qui applique une méthode range
et un filtre variable qui
applique une méthode contains
. Dans ce cas, la notation combinée n'est
plus possible et il devient nécessaire de définir les filtres au moyen de
deux références distinctes.
Un filtre qui est purement permanent ne sera pas repris dans l'arbre de sortie. Du point de vue de l'utilisateur final, ce filtre n'existe pas (il s'agit d'un filtre appliqué par l'application, mais non modifiable par l'utilisateur).
Un filtre variable est systématiquement repris dans l'arbre de sortie pour permettre à l'utilisateur final d'en modifier la condition (la valeur du filtre).
Toutefois si le filtre est à la fois permanent et variable, le filtre est repris au niveau de l'arbre de sortie, mais la valeur de filtre du filtre permanent n'est pas reprise dans l'arbre de sortie. Si l'utilisateur saisit une valeur de condition pour un filtre permanent et variable, alors le moteur va générer 2 clauses pour le champ : une clause pour le filtre permanent et une seconde pour le filtre variable.
Il est possible de définir une ou plusieurs valeurs par défaut pour le champ.
La ou les valeurs seront utilisées comme valeur de filtre par défaut. La
syntaxe est la même que pour la définition de valeur par défaut au niveau
des champs. Voir Champ (<field>
).
Il est possible de définir une liste d'options, c'est-à-dire une liste de
valeurs possibles pour le champ. La syntaxe est la même que pour la
définition d'options au niveau des champs. Voir
Champ (<field>
).
Autres propriétés
Il est possible de définir d'autres propriétés au niveau des champs (validation, pattern de formatage, etc.). Dans la version actuelle, les éventuelles propriétés supplémentaires définies au niveau des champs de filtre ne sont pas prises en compte pour les champs de filtre.
Pagination (<pagination>
)¶
Les règles de pagination sont constituées de 2 paramètres: un décalage de
base (offset
) et un nombre d'éléments (limit
).
1 2 3 4 |
|
L'attribut customizable="true"
indique que l'on autorise l'utilisateur à
modifier la pagination.
Dans l'exemple ci-dessus, la pagination découpe les tuples en portions de 30
lignes, auxquelles on applique un décalage de 10 (on commence à afficher à
partir du 11e tuple). Le paramètre offset
est optionnel, sa
valeur par défaut étant 0.
Dans la version actuelle, il n'est pas possible de spécifier les règles de
pagination autrement qu'en mode text
.
Prise en compte des modifications apportées à la descript
Dans la version actuelle, les modifications apportées à la descript concernant les options d'arrangements ne sont pas directement prises en compte lors du reset sur les dossiers ouverts. Elles sont bien chargées au niveau du moteur, mais elles ne sont prises en compte pour un dossier que lors de son ouverture.
Si le reset est réalisé alors qu'un utilisateur travaille sur un dossier, les modifications apportées ne seront donc pas immédiatement prises en compte pour cet utilisateur. Cela vient du fait que l'utilisateur a potentiellement modifié des paramètres de tri, de filtre ou de pagination. Les options d'arrangement définies par l'utilisateur sont donc liées à l'instance de document chargée dans la session de l'utilisateur et il n'est pas souhaitable qu'elles soient changées tant que le dossier reste édité par l'utilisateur.
En tant que développeur, il faut donc penser à fermer et rouvrir un dossier pour tester les modifications apportées à la descript.
Champ (<field>
)¶
L'élément <field>
permet de décrire un champ. Un champ est constitué des
éléments suivants:
-
attribut
name
: nom du champ interne, utilisé pour référencer le champ au niveau des ressources de langues (i18n) -
attribut
column
: nom de la colonne enregistrant la donnée du champ en base de données; si non défini, alors un attributdatatype
est requis -
attribut
idform
: forme de la valeur; cet attribut permet de surcharger la valeur définie dans l'entréeadmin.tupleIdForm
du fichier de configuration.Attention, l'utilisation de cet attribut est assez encadré: il n'est pas permis de modifier le format d'un identifiant de dossier. Ce dernier doit avoir le format déclaré dans l'entrée
admin.tupleIdForm
de la configuration et avoir un type de colonne compatible avec ce format. Cette limitation vient du fait que le moteur doit pouvoir gérer le lock des dossiers dans le modèle de concurrence pessimiste. Cela nécessite que tous les identifiants de dossiers utilisent un même format et, par conséquence, un même type de colonne au niveau de la base de données. -
attribut
datatype
: cet attribut n'est à utiliser que si on ne définit pas decolumn
mais que l'on souhaite malgré tout que la valeur du champ soit conservée en mémoire (dans la session). Sicolumn
etdatatype
ne sont pas renseignés, le moteur considère que le champ n'enregistre pas de valeur (c'est par exemple le cas si le champ doit représenter un bouton, un message statique, etc.). Cet attribut renseigne le moteur sur le type de donnée géré par le champ; il reprend le même type de valeur que l'attributtype
de la colonne qui aurait été référencé parcolumn
(voir document Schéma de base de données pour plus d'infos sur cet attribut) -
attribut
type
: facultatif, type de champ de formulaire html; quelques types sont réservés et interprétés par le moteur, les autres types sont libres. Ainsi:hidden
: réservé pour désigner un champ caché (le moteur a besoin de savoir si un champ est un champ caché pour gérer correctement les filtres de groupes multi)binary
oubin
oufile
: réservé pour désigner un champ binaire (le moteur a besoin de savoir si un view affiche des champs binaires; cela permet d'indiquer dans l'arbre de sortie le type de formulaire attendu en réponse: en présence de fichiers, il indiquera un type multipart, sinon il indiquera un type url-encoded)
Le moteur ne s'attend pas à recevoir de champs HTML qui référencent les
mainfield
. Lorsque cela arrive, le moteur affiche un message d'avertissement du genre "Ignore value update of id fieldmodelName/groupName/fieldName
. HTML posted form shouldn't contain inputs for fields being used as primary key. Please fix your stylesheets." lorsqu'il fonctionne en mode "dev". Pour éviter ce message, nous recommandons de ne pas spécifier de type pour ces champs et d'adapter la feuille de style pour qu'elle ne crée pas d'input HTML dans le cas où le type est vide. Les champs avec un type non défini ou vide sont considérés comme deshidden
du point de vue des options d'arrangement. -
attribut
label
: facultatif, libellé associé au champ. Cet attribut permet de spécifier le libellé à reprendre dans l'arbre de sortie dans le cas où aucun libellé d'internationalisation n'est trouvé dans le bundle associé à la descript. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
description
: facultatif, texte court décrivant le champ. Cet attribut permet de spécifier le descriptif à reprendre dans l'arbre de sortie dans le cas où aucun libellé d'internationalisation n'est trouvé dans le bundle associé à la descript. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
attribut
placeholder
: facultatif, texte à afficher dans le champ lorsque celui-ci est vide. Cet attribut permet de spécifier le libellé à reprendre dans l'arbre de sortie dans le cas où aucun libellé d'internationalisation n'est trouvé dans le bundle associé à la descript. L'attribut peut également être déclaré dans le namespacech.epilogic.ewt.i18n
si on veut forcer une référence de bundle de ressources. -
élément
formatting
: pattern de formatage du champ; le type de pattern utilisé doit être compatible avec le type de champ SQL utilisé dans la table -
élément
default
: valeur par défaut du champ -
élément
options
: liste d'options pour les listes déroulantes -
élément
policies
: règles de gestion des droits -
élément
validation
: règles de validation de la donnée en provenance du formulaire HTML (voir chapitre Validation de données client ci-dessous) -
attributs
indexstore
etindexmode
: ils remplissent le même rôle que pour le niveaumodel
, et viennent surcharger la valeur des attributs correspondants éventuellement définis au(x) niveau(x) parent(s). -
élément
arrangement
: règles à appliquer lorsqu'un tri ou un filtre est demandé sur le champ; voir Arrangement. -
attribut
policy
: facultatif, nom de la ou des policies à appliquer au champ. Il est possible de référencer plusieurs policies en les séparant par une virgule. Les policies référencées via cet attribut s'ajoutent à celles définies ou référencées via l'élémentpolicies
. -
élément
properties
: propriétés de champs; ces propriétés ne sont pas traitées par le moteur (hormis les éléments d'internationalisation; voir le chapitre relatif à ce sujet pour plus de détails), mais transmis tels quels dans l'arbre de sortie. Les attributs peuvent être du texte simple, du XML, du JSON, du SCSS, etc. -
élément
multiple
: cet élément est à utiliser dans le cas où un champ est susceptible de recevoir plusieurs valeurs. C'est par exemple le cas des champs de type case à cocher ou des champsselect
avec attributmultiple
: le formulaire html peut envoyer plusieurs champs du même nom avec des valeurs différentes. Lorsque l'élément est spécifié, le moteur sait qu'il doit encoder les valeurs reçues du formulaire html.- attribut
format
: Il est possible d'indiquer le format d'encodage au moyen de l'attributformat
. Celui-ci peut prendre l'une des valeurs suivantes:separated
: Les valeurs sont encodées dans une chaîne de caractères séparés par un délimiteur. C'est le format par défaut.json
: Les valeurs sont encodées au format jsonbitset
: Les valeurs sont encodées dans un bitset, c'est-à-dire une valeur entière qui "encode" les différentes valeurs au moyen d'un opérateur OR. Ce format n'est utilisable qu'avec des valeurs numériques. Il n'est pas conseillé d'activer le flagdetail
lorsque les valeurs sont grandes, car il peut être coûteux pour le moteur de reconstruire la liste de valeurs.
- attribut
separator
: Dans le cas où le format estseparated
, l'attributseparator
permet de spécifier le séparateur. Le séparateur par défaut est la virgule (,
) - attribut
detail
: Ce flag demande au moteur de générer un détail de valeurs dans l'arbre de sortie. Cela se manifeste par un élémentvalues
qui est ajouté dans l'output. Par défaut ce paramètre estfalse
.
Le mode
multiple
est fonctionnel uniquement pour les valeurs litérales. Les champs binaires ne sont pas supportés en mode multiple.Nommage des champs multiples
Un champ
<select multiple>
est envoyé différemment dans la requête POST selon le type de formulaire et les options sélectionnées dans la liste déroulante duselect
.Si aucune option n'est sélectionnée, le champ n'est pas repris dans le formulaire. Le moteur ne sera donc pas informé des éventuelles modifications apportées par l'utilisateur.
Si une seule option est sélectionnée, le champ est envoyé comme un champ standard et le moteur peut être trompé quant à la nature du champ.
Pour que le moteur soit capable de détecter ces deux cas de figure, il est recommandé d'appliquer les deux principes suivants:
- Ajouter un suffixe
[]
à la fin du nom de champ duselect
multiple: cela permet au moteur de savoir que le champ est un champ multiple - Ajouter un champs caché du même nom que le select, mais sans
suffixe: cela permet au moteur d'avoir une valeur par défaut à
enregistrer dans le cas où le
select
multiple ne contient aucune option sélectionnée. Lorsqu'il reçoit à la fois le champ avec suffixe et le champ sans suffixe, le moteur ignore la valeur du champ sans suffixe.
Voici ce que cela donne au niveau de la page HTML lorsque l'on applique les deux principes ci-dessus:
1 2 3 4 5 6 7 8 9 10
<input name="EWT:DATA-7C50CCC0C2CE0C12D3F7B5A9B4419CE1" value=""/> <select name="EWT:DATA-7C50CCC0C2CE0C12D3F7B5A9B4419CE1[]" id="input-7C50CCC0C2CE0C12D3F7B5A9B4419CE1" multiple="" data-allow-clear="true" data-tags-select="true"> <option value="" hidden="hidden" disabled=""/> <option value="1">Création</option> <option value="2">Ouvert</option> <option value="3">Pris en charge</option> <!-- ... --> </select>
- attribut
Validation de données client¶
La validation consiste à contrôler voire à nettoyer une valeur en provenance
d'un champ de formulaire envoyé au moteur. Le moteur autorise donc deux
types d'actions (à définir dans un attribut action
):
-
check
: la validation consiste à contrôler la valeur; une erreur est retournée à l'utilisateur si le contrôle échoue -
sanitize
: la validation consiste à nettoyer la valeur pour qu'elle ne contienne aucun élément susceptible de provoquer une injection XSS; lorsque la valeur est modifiée suite à une opération de nettoyage, un message d'avertissement est retourné à l'utilisateur
L'action par défaut est check
. Cela signifie que si l'attribut action
n'est pas spécifié, le moteur considère que la validation doit effectuer un
contrôle.
Pour chaque type d'action, on indiquera la méthode de validation au moyen de
l'attribut method
. Ce dernier est obligatoire.
Contrôle (check
)¶
Le contrôle de valeur est possible de trois façons:
-
Au moyen d'une règle prédéfinie (
rule
) : Une règle est une description d'autorisations ou de refus relatifs au contenu du champ. La règle peut être définie sous différentes formes.-
La forme la plus simple est une chaîne de caractères qui référence des règles pré-définies du moteur. Dans la version actuelle, Ewt intègre les règles de contrôle pré-définies suivantes:
MANDATORY
: indique que le champ est obligatoire et ne peut pas être laissé videEMAIL
: indique que la valeur du champ doit être une adresse email
À ces règles s'ajoutent également les règles pré-définies dans la librairie Owasp (
FORMATTING
,BLOCKS
,STYLES
,LINKS
,TABLES
,IMAGES
). Voir le chapitre relatif ausanitize
ci-dessous pour avoir une description de ces règles.Précisons au passage qu'il est possible de cumuler plusieurs mots-clés au sein d'une même règle de validation. Pour ce faire, il suffit de séparer les différents mots-clés par une barre verticale
|
(p.ex.EMAIL|MANDATORY|FORMATTING
) -
La règle peut être définie en json. Cette forme permet un contrôle plus avancé des autorisations et des refus. La notation json reprend la syntaxe de l'action
sanitize
décrite plus bas dans ce document. Un exemple decheck
utilisant une règle définie en json est également donné dans les exemples plus bas.
-
-
Au moyen d'une expression régulière (
regex
) : On spécifie une expression régulière qui représente la valeur. Si la valeur correspond à l'expression, la validation réussit, sinon elle échoue et une erreur est signalée. -
Au moyen d'un script (
script
) : On décrit une expression de script qui réalise le test de la valeur. Le moteur s'attend à ce que le script retournetrue
si la validation réussit etfalse
si elle échoue. La valeur à tester est disponible via la variable$$.data
. Elle peut également être obtenue via les notations${data:nom_du_champ}
ou#nom_du_champ
.
Nous tenons à préciser que la méthode script
nécessite davantage de
ressources et est susceptible de sensiblement ralentir l'application.
Nettoyage (sanitize
)¶
Le nettoyage de valeur est possible de deux façons:
-
Au moyen d'une règle (
rule
) : Comme dans le cas de l'actioncheck
, les règles dusanitize
peuvent être définies de différentes façons.-
La forme la plus simple est une chaîne de caractères qui référence des règles pré-définies de la librairie (dans la nomenclature Owasp, ces règles sont appelés policies, mais nous évitons ce terme ici pour ne pas créer de confusion avec les policies de gestion des droits d'accès).
Par défaut, Owasp applique une politique de validation stricte, mais il est possible de spécifier des règles qui autorisent certains types d'éléments (sanitizers).
Les sanitizers prédéfinis sont:
FORMATTING
: Autorise les éléments standard de formatage comme <b>,<i>, etc.BLOCKS
: Autorise les éléments standard de blocs tels que < p>, <h1>, etc.STYLES
: Autorise certaines propriétés CSS sûres dans les attributs du typestyle="..."
LINKS
: Autorise HTTP, HTTPS, MAILTO et les liens relatifsTABLES
: Autorise les éléments standard liés aux tablesIMAGES
: Autorise les éléments <img> à partir de HTTP, HTTPS et les sources relatives
Les sanitizers ci-dessus peuvent être chaînés comme dans l'exemple suivant:
FORMATTING|BLOCKS|STYLES|LINKS|TABLES|IMAGES
-
La règle peut être définie en json. Cette forme permet un contrôle plus avancé des autorisations et des refus. L'objet json attendu est soit un dictionnaire (map), soit un tableau (array) de dictionnaires. Les dictionnaires peuvent reprendre les méthodes de la classe HtmlPolicyBuilder.
Les propriétés reconnues sont les suivantes:
-
allowAttributes
: Autorise les attributs indiqués. La règle peut être affinée au moyen d'une propriétématching
qui permet de spécifier une expression régulière de filtre,onElements
qui permet d'inventorier les éléments sur lesquels la règle s'applique, ainsi qu'une propriétéglobally
indiquant que la règle s'applique à tout type d'élément (sous réserve qu'il respecte la regex de la propriétématching
). Exemple:{ "allowAttributes": [ "id" ], "matching": "[a-zA-Z0-9\\:\\-_\\.]+", "globally": true }
-
allowCommonBlocElements
: Correspond àBLOCKS
lorsque la règle est définie en tant que chaîne de caractères. Exemple:{ "allowCommonBlocElements": true }
-
allowCommonInlineFormattingElements
: Correspond àFORMATTING
lorsque la règle est définie en tant que chaîne de caractères. Exemple:{ "allowCommonInlineFormattingElements": true }
-
allowElements
: Autorise les éléments indiqués. Exemple:{ "allowElements": [ "i", "b" ] }
-
allowStandardUrlProtocols
: Autorise les protocols d'URLhttp
,https
etmailto
. Exemple:{ "allowStandardUrlProtocols": true }
-
allowStyling
: Correspond àSTYLES
lorsque la règle est définie en tant que chaîne de caractères. Exemple:{ "allowStyling": true }
-
allowTextIn
: Autorise le texte dans les éléments indiqués. Exemple:{ "allowTextIn": [ "span", "div" ] }
-
allowUrlProtocols
: Ajoute un set de protocols autorisés dans les attributs d'URL. Exemple:{ "allowUrlProtocols": [ "ssh", "ldap", "ldaps" ] }
-
allowWithoutAttributes
: En supposant que les éléments donnés sont autorisés, permet de les faire apparaître sans attributs. Exemple:{ "allowWithoutAttributes": [ "i", "b" ] }
-
disallowAttributes
: Inverse deallowAttributes
, les propriétésmatching
,onElements
etglobally
sont également applicables disallowElements
: Refuse les éléments indiquésdisallowTextIn
: Refuse le text dans les éléments indiquésdisallowUrlProtocols
: Inverse deallowUrlProtocols
disallowWithoutAttributes
: Interdit les éléments indiqués d'apparaître sans attributs-
requireRelNofollowOnLinks
: Ajouterel=nofollow
sur les liens. Exemple:{ "requireRelNofollowOnLinks": true }
-
requireRelsOnLinks
: Ajouterel="..."
aux balises<a href="...">
. Exemple:{ "requireRelsOnLinks": true }
-
skipRelsOnLinks
: Permet d'éviter l'ajout derel="..."
au liens indiqués. Exemple:{ "skipRelsOnLinks": [ "noopener", "noreferrer" ] }
Une règle est donc un tableau qui contient différents objets utilisant la syntaxe ci-dessus. Un exemple de règle est donné plus bas dans les exemples.
-
-
-
Au moyen d'un script (
script
) : On décrit une expression qui retourne la valeur nettoyée de la valeur d'entrée. Si la valeur en sortie est différente de la valeur initiale, un avertissement est envoyé à l'utilisateur.
Nous tenons à préciser que la méthode script
nécessite davantage de
ressources et est susceptible de sensiblement ralentir l'application.
Source de la validation¶
Pour chaque règle de validation, on peut indiquer à l'aide de l'attribut
source
sur quelle valeur la règle doit s'appliquer. L'attribut peut
prendre les valeurs:
raw
: représente la valeur brute reçue du formulaire html, c'est-à-dire la valeur avant nettoyage des éventuels éléments de formatage; une date brute pourra par exemple être "15/10/2022"std
: représente la valeur standardisée, éventuellement retraitée et normalisée dans le cas où le champ utilise un formatage; dans le cas de notre date, on aura la valeur "2022-10-15".
Par défaut, c'est-à-dire si aucune source n'est spécifiée, la validation s'effectue sur la valeur standardisée, pour les raisons évoquées dans la note ci-dessous.
Remarques par rapport au formatage
Certains règles de validation peuvent entrer en conflit avec les règles de formatage de valeurs. Il est donc important d'indiquer si la règle de validation doit porter sur la valeur brute ou la valeur remise en forme standardisée.
Il est recommandé de s'appuyer sur la forme standard car il peut arriver que l'utilisateur apporte des variations dont il faudrait tenir compte dans la règle de validation. Il peut également arriver que la forme de la donnée brute soit différente selon la locale du navigateur du client. L'élaboration d'une règle de validation sur la donnée brute peut par conséquent vite devenir un casse-tête. La forme standard est plus simple à valider.
Message d'erreur ou d'avertissement¶
Lorsqu'une règle de validation n'est pas respectée, le moteur génère un
message d'erreur (dans le cas des check
) ou d'avertissement (dans le cas
des sanitize
). Un message prédéfini est utilisé, mais il est possible de
surcharger ce dernier au moyen d'un message personnalisé. Pour ce faire, il
suffit d'ajouter un attribut message
qui contient le message à afficher.
Il est possible de référencer une entrée de bundle en définissant cet
attribut dans le namespace ch.epilogic.ewt.i18n
(en préfixant l'attribut
avec i18n:
). Le libellé du message sera donc à spécifier au niveau des
fichiers de langues du dossier i18n
de l'application. Comme indiqué plus
haut, le niveau du message dépend du type d'action. Il est possible de
spécifier explicitement le niveau du message à afficher au moyen de
l'attribut level
. Celui-ci peut alors prendre les valeurs error
, warn
ou info
.
Quelques exemples de règles de validation:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Dans le dernier exemple ci-dessus, on définit une règle de contrôle qui
vérifie que la valeur n'autorise que la présence de balise <i>
et que
le seul attribut autorisé pour cette dernière est l'attribut class
.
Types de champs¶
L'élément <type>
défini au niveau d'un champ correspond plus ou moins au
type d'input à afficher dans le formulaire html. Ewt n'agit pas sur ce type
et le reprend tel quel dans l'arbre de sortie. Il est donc du ressort de la
feuille de style d'interpréter ce type pour construire les champs du
formulaire affiché.
Ewt s'appuie toutefois sur le type pour identifier deux catégories de champs:
- les champs cachés : Ewt considère qu'un champ est caché si son type est
hidden
- les champs binaires : Ewt considère qu'un champ est un champ contenant des
données binaires si son type est
file
(il est également possible d'utiliserbinary
oubin
)
Notez que le type de champ peut avoir une incidence sur les règles de
formatage. En effet, sur une page HTML 5, il se peut que le client (le
navigateur) interprète le type et effectue de lui-même un formatage. C'est
le cas des types date
, time
, datetime-local
, number
, etc.
Il est donc important de tenir compte du type
et du formatting
de
manière cohérente. Le chapitre qui suit tente de donner plus d'éléments à ce
sujet.
Formatting¶
Le formatting consiste à mettre en forme une donnée pour qu'elle soit plus
facilement lisible à l'écran. Il est possible de gérer le formatting au
niveau de l'application via la définition d'un pattern de formatage (élément
<formatting>
). Il est également possible de s'appuyer sur HTML5 pour gérer
le formatage de valeurs (en particulier les dates et les heures - le type
prévu pour les nombres n'est quant à lui pas idéal pour le formatage). Ces
deux aspects sont présentés ci-dessous.
Nombres¶
Le formatting des nombres peut utiliser les règles de pattern définies dans
la documentation de la classe java
DecimalFormat.
Le champ HTML devra alors être de type text
, avec éventuellement un
pattern
de validation (voir
pattern).
Remarque concernant l'élément input type="number"
en HTML5
Le type de champ number
est prévu pour gérer des nombres. Le navigateur
effectuera automatiquement un filtre des entrées pour n'autoriser que la
saisie de caractères permettant de définir un nombre. Toutefois il faut
émettre quelques réserves:
- Ce champ ne gère pas le formatting de nombres; selon la
documentation de mozilla, l'attribut
pattern
des inputs HTML n'est pas adapté à ce type de champ. Par conséquent, le moteur enverra toujours une valeur non formatée dans l'arbre de sortie lorsqu'il détecte qu'un champ utilise le typenumber
. - Le champ
input type="number"
peut être une source d'erreur chez certains utilisateurs : si un utilisateur saisit un champ de ce type, puis souhaite scroller dans le formulaire à l'aide de la souris, il se peut que la valeur de l'input soit modifiée par le scroll mais que l'utilisateur ne s'en rende pas compte.
Pour ces raisons, nous recommandons de définir un champ numérique avec formatage de la manière suivante:
<field name="monChamp" type="text" column="MonChamp">
<formatting>#,##0.00</formatting>
</field>
Algorithme de suppression du formatage
La gestion du formatage des nombres est intrinsèquement liée à celui de
la locale. En effet, le rendu final d'une valeur formatée dépend de la
locale pour laquelle la valeur est formatée. Pour ne rien arranger, la
représentation peut également changer en fonction de la valeur de la
propriété java.locale.providers
: par exemple la valeur COMPAT
force
java à appliquer les règles de locale telles que définies pour java 8.
Ainsi, une valeur 1234567.89
aura différentes représentations en
fonction de la locale utilisée, par exemple pour la locale "fr-CH",
cette valeur sera formatée en 1 234 567,89
en mode standard et
1'234'567.89
en mode COMPAT
.
D'autres types de séparateurs de milliers et de délimiteurs de décimales sont également utilisés par d'autres locales, ce qui complique encore la tâche.
Partant de ce constat, un algorithme de suppression du formatage a été
mis au point dans Ewt pour tenter d'interpréter un nombre de la manière
la plus juste possible et qui soit le moins lié possible avec la locale.
Cette fonction cherche à retirer tous les délimiteurs et utilise le .
comme délimiteur de décimales. L'algorithme est amené parfois à faire des
suppositions. Ainsi, s'il trouve un nombre comportant différents types de
délimiteurs, il considérera que le délimiteur placé le plus à droite dans
le nombre sera le délimiteur de décimales. Cela qui peut donner lieu à
des interprétations surprenantes. Ainsi, une valeur 1a2a3b4
sera
interprétée comme le nombre 123.4
. Il peut donc se révéler utile de
mettre en place des contrôles de validité de la valeur brute afin de
limiter les risques d'erreur d'interprétation des données lors de la
suppression de formatage.
Date¶
Il est possible de déléguer la gestion des dates à l'élément input
en lui
spécifiant un type date
. Ce type de champ HTML5 filtre la saisie et
fournit nativement un picker de date. Ewt reconnaît ce type et adaptera la
valeur au niveau de l'arbre de sortie pour qu'elle soit compatible avec ce
type de champ.
Il est également possible d'utiliser une règle de formatting via l'élément
<formatting>
. Dans ce cas, la syntaxe de pattern est définie par la classe
SimpleDateFormat.
Il n'est pas recommandé d'utiliser à la fois un pattern et un type d'input
spécifique. En effet, l'input
s'attend à recevoir les données brutes non
formatées.
Exemple:
1 2 3 4 5 6 7 |
|
Remarque concernant le format des dates
En interne, Ewt gère les dates selon le format ISO 8601 au format standard (par opposition au format basique). Concrètement, la date du 15 octobre 2015 sera enregistrée dans la forme "2015-10-15".
Time¶
Idem que pour "Date". Dans ce cas, le type de l'élément input
sera time
.
Si l'option pattern est choisie, la syntaxe doit respecter les règles définies par la classe SimpleDateFormat.
Il n'est pas recommandé d'utiliser à la fois un pattern et un type d'input
spécifique. En effet, l'input
s'attend à recevoir les données brutes non
formatées.
Exemple:
1 2 3 4 5 6 7 |
|
Remarque concernant le format des heures
En interne, Ewt gère les heures selon le format ISO 8601. Concrètement, l'heure 9 heures 28 minutes 32 secondes sera enregistrée dans la forme "09:28:32".
Timestamp¶
Idem que pour "Date". Dans ce cas, le type de l'élément input
sera
datetime-local
.
Si l'option pattern est choisie, la syntaxe doit respecter les règles définies par la classe SimpleDateFormat.
Il n'est pas recommandé d'utiliser à la fois un pattern et un type d'input
spécifique. En effet, l'input
s'attend à recevoir les données brutes non
formatées.
Exemple:
1 2 3 4 5 6 7 |
|
Remarque concernant le format des timestamps
En interne, Ewt gère les heures selon le format ISO 8601. Un timestamp sera par exemple représenté ainsi : "2015-10-15 09:28:32.123". La précision au niveau des milli/micro/nano secondes varient en fonction de la machine virtuelle java et de l'OS utilisé.
Valeur par défaut¶
La définition de valeur par défaut, c'est-à-dire la valeur attribuée au
champ à la création d'un tuple, se fait au moyen de l'élément <default>
.
Cet élément prend plusieurs formes en fonction du mode d'obtention de la
valeur (texte, sql ou script).
Valeur textuelle (mode="text"
)¶
La valeur est passée via l'élément <value>
. Elle peut contenir des
références de variables ou de données:
1 2 3 |
|
Valeur obtenue par SQL (mode="sql"
)¶
L'élément <value>
attend une requête SQL. Les éventuels paramètres à
passer au prepared statements
sont à placer dans des éléments <param>
. La requête SQL peut référencer
les paramètres de façon anonyme (avec le caractère ?
) ou par nom (par
exemple avec :myparam
). Il n'est par contre pas possible de mélanger les
deux formes de référence au sein d'une même requête.
Les paramètres doivent être typés au moyen de l'attribut type
. Dans le cas
de références par nom, il faut également spécifier un attribut name
.
Exemple de requête utilisant des références anonymes. Dans ce cas, il doit y avoir autant de paramètre que de références.
1 2 3 4 5 6 |
|
Exemple de requête utilisant des références par nom. Dans l'exemple ci-dessous, on voit que le paramètre "cat" est référencé deux fois dans la requête.
1 2 3 4 5 6 |
|
Valeur calculée par script (mode="script"
)¶
L'élément <value>
attend un script. Dans la version actuelle, le script
doit être inscrit inline et ne peut pas prendre de paramètre.
1 2 3 |
|
Il est prévu de permettre une référence de fichier de script et le passage de paramètres dans une version future.
Champs non persistents
Comme indiqué en début de section, les valeurs par défaut sont calculées et assignées au champ à la création des tuples uniquement. Il n'est donc pas approprié d'utiliser ce type d'élément pour assigner une valeur à un champ non persistent, c'est-à-dire un champ purement informatif mais non enregistré en base de données.
En effet, dans ce cas la valeur serait bien assignée au champ à la création du tuple (typiquement à la création d'un dossier ou d'un tuple de groupe multi), mais elle ne le serait plus par la suite lorsque l'on recharge le tuple (à l'ouverture de dossier).
Pour assigner une valeur à un champ non persistent, il est recommandé
d'utiliser un script déclenché par les notifications doc-create
et
doc-open
. Voici un exemple de test script:
...
<notifications>
<notification name="doc-create">calcMemFields</notification>
<notification name="doc-open">calcMemFields</notification>
</notifications>
...
1 2 3 4 5 6 7 8 9 10 11 |
|
Il est également possible d'utiliser les notifications doc-save
ou
gen-output
si la valeur est susceptible de changer durant le temps où
le dossier est ouvert.
Listes d'options¶
Certains champs peuvent proposer une liste de valeurs possibles. C'est le cas des listes déroulantes, des listes de cases à cocher, des listes de puces, etc. La liste d'options associe généralement un libellé à une clé. Du point de vue de l'application, la clé correspond à la valeur enregistrée en base de données alors que le libellé est un texte sans réelle signification du point de vue métier et qui peut également est adapté en fonction de la locale.
Pour définir une liste d'options, on utilise l'élément <options>
et on
indique le type de liste décrite au moyen de l'attribut mode
, qui
peut prendre les valeurs sql
ou inline
:
-
sql
: Ce mode permet de définir la liste d'options au moyen d'une requête SQL. La requête peut utiliser la notation prévue pour les prepared statements. Dans ce cas, les paramètres sont à placer dans des élémentsparam
. Exemple:1 2 3 4 5
<options mode="sql" level="field"> <value>SELECT idVendeur,resumeText FROM Vendeur WHERE idVendeur=? ...</value> <text:param type="int">${data:benevoles.idVendeur}</text:param> <text:param>#benevoles.idVenteEchange</text:param> </options>
Dans l'exemple ci-dessus, le premier paramètre référence un champ via la notation
${data:xxx}
. Ce type de référence retourne toujours une valeur de typestring
, c'est pourquoi un attributtype
est exigé afin d'indiquer au moteur le type de la valeur à reprendre dans le statement. Le second paramètre utilise quant-à-lui la notation "sharp". La valeur retournée par cette référence étant déjà typée, il n'est pas nécessaire de spécifier le type dans ce cas. On note également la présence du préfixetext:
qui indique au moteur qu'il doit effectuer la substitution des références trouvées.Il est également possible de référencer les paramètres par nom. Dans ce cas, les éléments
<param>
doivent posséder un attributname
qui indique le nom.1 2 3 4 5
<options mode="sql" level="field"> <value>SELECT :idVendeur,resumeText FROM Vendeur WHERE idVendeur=:idVendeur ...</value> <text:param name="idVendeur" type="int">${data:benevoles.idVendeur}</text:param> <text:param name="idVenteEchange">#benevoles.idVenteEchange</text:param> </options>
Attention, il n'est pas possible de mélanger les références anonymes (avec
?
) et les références nommées (avec:myparam
) au sein d'une même requête. -
inline
: Ce mode permet de spécifier les valeurs directement dans la descript, en dur. Exemple:1 2 3 4 5
<options mode="inline" level="field"> <i18n:option value="val1">some.label.from.language.bundle</i18n:option> <option value="val2">Libellé en dur</option> <text:option value="val3">user ${env:USERNAME}</text:option> </options>
Dans l'exemple ci-dessus, la première option est préfixée de
i18n:
. Cela indique au moteur que le texte de l'élément est une référence de bundle. Le moteur se chargera de substituer cette référence par le libellé approprié. La seconde option passe une valeur litérale en dur.
L'attribut level
permet d'indiquer à quel niveau dans l'arbre de sortie on
souhaite voir apparaître les valeurs d'options du champ. Cela permet d'éviter
les répétitions de listes d'options, en particulier sur les groupes
"multi". L'attribut level
peut prendre les valeurs suivantes:
descript
: La liste d'options est reprise dans l'élémentfield
de la partiedescript
de l'arbre de sortie; cette option est souhaitable lorsque la liste d'option peut être longue (ce qui évite de la répéter sur chaque dossier ou chaque champ) et que ses données ne sont pas dépendantes d'un dossier ou d'un champ en particulier.doc
oudocument
: La liste d'options est reprise au niveau du dossier; cette option est souhaitable lorsque la liste peut être longue et dépendante du dossier ouvertgroup
: La liste d'options est reprise au niveau du groupe; cette option est souhaitable lorsque la liste peut être longue et dépendante du groupetuple
: La liste d'option est reprise au niveau du tuple; cette option est souhaitable lorsque la liste est dépendante du champ ou d'un autre champ du tuple, et pour autant que la liste d'option ne soit utilisée que par un seul champ du tuplefield
: La liste d'option est reprise au niveau du champ; cette option est souhaitable lorsque la liste est dépendante du champ ou d'un autre champ du tuple
La valeur par défaut du champ est field
.
Références¶
Il est possible de partager une liste d'options entre plusieurs champs d'un
même modèle. Par exemple, si une vue contient plusieurs champs qui doivent
afficher une même liste (une liste de statuts, une liste d'utilisateurs,
etc.), il n'est pas nécessaire de redéfinir la liste d'options sur chaque
champ, mais il est possible de référencer la liste d'un autre champ avec
l'attribut ref
. Cette référence doit se rapporter à une autre liste qui
sera identifiée au moyen d'un attribut id
. L'identifiant défini avec id
doit être unique sur toute la descript (le moteur générera une erreur s'il
détecte plusieurs listes avec un même id
).
Voici un exemple dans lequel on définit une liste d'utilisateurs au niveau
du groupe base
(lignes 5 à 7). Cette liste est référencée depuis le champ
"idAuteur" du groupe commentaires
(ligne 19):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Séparateur et valeurs supplémentaires¶
Par défaut en mode sql
, les options sont des couples formés d'une valeur (la
valeur réellement enregistrée dans le champ) et d'un libellé (le libellé
affiché à l'écran dans la locale de l'utilisateur). En réalité le libellé
peut être découpé en plusieurs éléments. Ainsi, pour une requête
select idUser, firstName, ' ', lastName from User order by firstName, lastName
le moteur retiendra idUser
comme valeur et la concaténation des éléments
firstName
, ' '
, lastName
comme le libellé.
Il peut cependant arriver que l'on doive récupérer des éléments supplémentaires, comme des éléments de style par exemple. Le moteur autorise l'ajout d'éléments supplémentaires à intercaler entre la valeur est le libellé. Si on souhaite par exemple récupérer un avatar en même temps que le nom et prénom, on peut modifier la requête précédente ainsi:
select idUser, avatar, '::', firstName, ' ', lastName from User order by firstName, lastName
Dans cette requête, on a ajouté deux colonnes:
- La colonne "avatar" qui sert à récupérer une donnée supplémentaire qui n'apparaîtra pas en tant que libellé. On pourrait également ajouter d'autres colonnes supplémentaires si on a besoin
- La valeur "::" qui fait office de séparateur entre les colonnes constituant le libellé et les autres
Par défaut, Ewt reconnaît le séparateur "::". Pour être considérée comme
séparateur, la valeur "::" doit figurer comme contenu entier de la colonne.
Ainsi le moteur ne détectera pas de séparateur dans une chaîne 'foo::bar'
.
Il est possible de personnaliser le séparateur. Pour ce faire, il suffit
d'ajouter un attribut separator
à l'élément options
et d'y inscrire le
nouveau séparateur. Celui-ci n'est valable que pour la liste d'options
actuelle.
L'utilisation de colonnes supplémentaires et de séparateur n'est possible
qu'en mode sql
. Les valeurs des colonnes supplémentaires sont ajoutées à
l'élément option
de l'arbre de sortie via des attributs val#
où #
est
un entier commençant à 1.
Voici un exemple complet de champ utilisant la notation expliquée ici:
<field name="idUtilisateur" type="label" column="IdUtilisateur">
<options mode="sql" level="descript" id="liste-utilisateurs">
<value>select IdUtilisateur, Avatar, '::', Resume from Utilisateur order by Resume</value>
</options>
</field>
Voici l'arbre de sortie généré pour le champ ci-dessus:
<field name="idUtilisateur" fullname="ticket_base_idUtilisateur" type="label"
label="Ouvert par" description="" placeholder="" cloneable="false"
datatype="string" maxlength="36">
<options id="liste-utilisateurs">
<option value="d6a47d61-41f9-4137-9a3f-3fa8045a914a"
val1="<?xml version="1.0" encoding="utf-8?><svg ...</svg>">
Edith Avuleur
</option>
<option value="e8228589-4d4e-402c-ab4a-be53b32f1974"
val1="<?xml version="1.0" encoding="utf-8?><svg ...</svg>">
Oussama Lairbon
</option>
<option value="2f19b510-462a-409c-a9fe-53118513a732"
val1="<?xml version="1.0" encoding="utf-8?><svg ...</svg>">
Maude Zarella
</option>
</options>
</field>
Champs binaires¶
Le servlet /web
supporte aussi bien les formulaires standards url-encoded
que les formulaires multipart. Ainsi les données binaires peuvent être
envoyées dans le même formulaire HTML que les données textuelles.
Pour uploader un fichier, on utilise un champ HTML de type
<input type="file">
. Le formulaire HTML lui-même sera de type multipart:
1 2 3 4 5 6 |
|
Aide au choix du type de formulaire
Le traitement de formulaire url-encoded est habituellement plus léger
que le traitement de formulaires multipart. Il est donc conseillé
d'éviter d'utiliser un formulaire multipart lorsque cela n'est pas
nécessaire. Au moment de construire l'arbre de sortie, Ewt
analyse les types de champs des dossiers à traiter et renseigne l'entrée
/output/session/formType
de l'arbre de sortie avec le type de
formulaire le plus adapté. La feuille de style qui construit le
formulaire HTML peut donc s'appuyer sur cette entrée pour alimenter
l'attribut enctype
du formulaire HTML.
Voici un exemple de code XSL qui construit la balise <form>
en
s'appuyant sur le type de formulaire proposé par Ewt:
1 2 3 4 |
|
Au niveau de la description de champ, le champ peut être décrit de la manière suivante:
<field name="monDocument" type="binary" column="MonDocument"/>
Le champ référence une colonne MonDocument
(définie au niveau de la
description de schéma de base de données) qui
pourra utiliser l'un des types "large object" prévus par le SGBD. Le
chapitre PostgreSQL et les blobs
explique les différents types de champs que propose PostgreSQL pour le
stockage des "large objects".
Attributs de fichiers¶
Nous avons abordé ci-dessus le stockage d'un fichier en base de données. Toutefois cela ne concerne que le corps du fichier. Il peut être utile de conserver également certaines metadonnées supplémentaires à propos du fichier, à commencer par son nom initial, sa taille et son mime-type. Ces informations doivent être stockées dans des champs distincts au niveau de la base de données, mais rattachés au fichier du point de vue de la descript.
Au niveau du fichier schema.xml
, les champs prévus pour l'enregistrement
des attributs sont à déclarer comme tout autre champ, par exemple:
1 2 3 |
|
Ewt s'attend toutefois à ce que ces champs se trouvent dans la même table que le champ qui stocke les données binaires. La conséquence de cela est que le champ doit posséder une valeur en base de données pour posséder des metadonnées. Il n'est pas possible d'associer des métadonnées à un champ virtuel. Il n'est pas non plus possible d'associer des métadonnées au champ faisant office de clé primaire.
Au niveau du fichier descript.xml
, on pourra alors référencer ces derniers
en tant qu'attributs pour le fichier selon la syntaxe suivante:
1 2 3 4 5 6 7 |
|
Remarque : Le préfixe file:
permet d'indiquer à Ewt qu'il s'agit d'un
attribut de fichier. Il est possible que le concept de metadonnée soit
étendu à l'avenir à d'autres types de champs. Le préfixe sert donc à
distinguer les attributs "réservés" par Ewt des autres.
À noter que l'enregistrement de metadonnées n'est pas obligatoire, mais recommandé.
Chargement des champs binaires
Lorsqu'on édite un dossier qui contient des champs binaires, Ewt ne télécharge pas systématiquement les données binaires. Les métadonnées sont bien récupérées dans la session, mais, pour des questions de performances, le contenu du large object n'est pas téléchargé. Celui-ci n'est téléchargé que lorsque cela est nécessaire, c'est-à-dire lorsque l'utilisateur le demande explicitement (par exemple en cliquant sur un lien de téléchargement, ou via un appel XHR).
Arrangement¶
Généralités¶
Le terme "arrangement" désigne les options de tri, de filtre et de pagination appliquées aux groupes multi. L'arrangement peut être exclusivement défini par l'application (au niveau de la descript), laissé au choix de l'utilisateur (l'interface utilisateur doit alors gérer les champs de saisie dans lesquels l'utilisateur peut définir les règles de tri, de filtre et de pagination), ou être un assemblage des deux (p.ex. des règles de filtres statiques utilisés pour filtrer le contenu d'une table et des options modifiables par l'utilisateur pour trier le contenu ou ajouter des filtres supplémentaires). De manière générale, les filtres définis par la descript ne peuvent pas être annulés ou remplacés par l'utilisateur. Ils sont fixes.
Au niveau de la descript, les règles d'arrangement sont définies au moyen
d'un élément arrangement
au niveau de l'élément group
. Ces règles
peuvent être complétées par des règles d'arrangement au niveau des éléments
field
. Pour donner l'idée générale:
- les règles d'arrangement définies au niveau du
group
indiquent quelles types d'arrangement l'application doit gérer - les règles d'arrangement définies au niveau des
field
indiquent avec quelle valeur ces arrangements peuvent se faire.
Arrangement au niveau group
¶
L'élément arrangement
(niveau group
) permet donc d'indiquer au moteur quel
critère de tri et quelle condition de filtre il doit appliquer pour le champ
lorsqu'un arrangement de tuples est demandé au niveau du groupe.
Exemple de règles d'arrangement définies au niveau group
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Arrangement au niveau field
¶
Par défaut (c'est-à-dire si aucune règle d'arrangement n'est définie au niveau du champ), le moteur s'appuie sur la valeur brute du champ, c.-à-d. la valeur en base de données. Cela fonctionne bien lorsque le champ contient une valeur numérique ou une chaîne de caractères standard. Par contre c'est problématique lorsque le champ est une référence vers un autre dossier ou lorsque l'on souhaite trier/filtrer sur une version modifiée de la valeur brute (p.ex. une date ou une valeur composée, comme une valeur "nom + prénom").
Par exemple, si un groupe demande un arrangement qui trie selon une colonne
idVendeur
et que ce dernier est une référence d'identifiant, un tri
standard aurait pour conséquence d'ajouter une clause ORDER BY idVendeur
ASC
à la requête d'extraction des tuples, ce qui n'est pas très judicieux
car il y a toutes les chances que l'utilisateur ne comprenne pas le tri et
pense que le tri ne fonctionne pas. À la place, il est plus efficace de trier
d'après le nom ou le prénom du vendeur que référence cet identifiant.
C'est ici qu'intervient l'élément arrangement
défini au niveau du field
.
Il permet justement de spécifier quel critère appliquer lorsqu'un groupe
demande un tri ou un filtre sur le champ et que la valeur brute de ce
dernier n'est pas exploitable directement.
1 2 3 4 5 6 7 8 9 10 |
|
Dans l'exemple ci-dessus, on définit une règle pour le tri qui s'appuie
sur les champs nom
et prenom
du modèle vendeur
(le moteur s'attend à
trouver ces champs dans le groupe principal du modèle), ainsi qu'une règle
de filtre qui s'appuie sur une clause SQL à appliquer sur la table
Vendeur
. Le fait d'utiliser le mode field
pour le tri et le mode sql
pour le filtre est un exemple, mais l'inverse est également possible.
Il est même possible de combiner les deux types d'arrangement en une seule
règle. Dans ce cas on spécifierait le type selon la forme
type="sort,filter"
.
Mode SQL
Arrêtons-nous quelques instants sur le mode sql
pour aborder différents
aspects:
- Typage
- Les clauses SQL définies au moyen de l'élément
value
doivent être typées. En effet, le moteur n'est pas en mesure de déterminer le type attendu par l'expression SQL et demande que celui-ci soit clairement spécifié pour permettre le passage de valeur selon la notation des prepared statements, afin d'éviter tout risque d'injection. Le type attendu par la clause SQL doit donc être spécifié à l'aide de l'attributtype
. La valeur de cet attribut doit contenir le type. Il n'est par contre pas nécessaire de spécifier la taille. L'exemple ci-dessus déclare le typevarchar
sans mentionner sa longueur. - Table
-
Dans le mode SQL, lorsqu'on souhaite référencer des colonnes d'une autre table comme dans l'exemple ci-dessus, on doit préfixer les noms de colonnes avec un nom de table. Ce nom de table doit être spécifié via l'attribut
table
. Dans l'exemple ci-dessus on a indiqué le nom "Vendeur" (qui correspond au vrai nom de la table), mais on peut très bien spécifier n'importe quel nom en réalité. En définissant un attributtable="HelloWorld"
, on pourrait alors écrire la clause SQL ainsi:<value type="varchar">concat(HelloWorld.Nom, HelloWorld.Prenom)</value>
En effet en présence d'arrangement selon le mode SQL le moteur construit une requête dans laquelle il intègre les tables externes à l'aide de jointures gauches (
LEFT JOIN
). Pour éviter les doublons, les noms de tables sont affublés d'un alias (généré dynamiquement). Le moteur doit alors adapter le clause SQL spécifiée dans le champ pour remplacer le nom de la table par le nom de l'alias.À noter en passant qu'il est également possible de découper la clause en deux:
<value type="varchar">HelloWorld.Nom</value> <value type="varchar">HelloWorld.Prenom</value>
Cette notation est d'ailleurs préférable pour des raisons de compatibilité. En effet, l'utilisation du mot-clé
concat
comme dans l'exemple initial peut poser des problèmes sur certains SGBD. Il n'est par exemple pas reconnu sur SQLite.On peut se passer de spécifier un nom de table lorsque la clause n'implique pas de référence à une colonne. Par exemple la clause suivante est autorisée:
1 2 3
<rule type="sort" mode="sql"> <value>1</value> </rule>
Sur certains SGBD, cette clause signifie que l'on souhaite trier selon la première colonne du
SELECT
. Attention toutefois, il s'agit ici d'un exemple pour illustrer le propos, mais ce type de notation n'est pas recommandé car l'ordre des colonnes utilisées dans la requête générée par le moteur peut changer d'un appel à l'autre (en particulier si on rajoute de nouveaux champs dans la descript).
Notons enfin que le mode SQL autorise l'utilisation de paramètres selon la
même notation que pour les éléments option
ou default
du champ.
Vue (<view>
)¶
Une view est un assemblage de groupes. Elle permet de décrire comment afficher les éléments à l'écran, où plutôt, elle permet de savoir quelles données Ewt doit fournir dans l'arbre de sortie en fonction du style à utiliser.
Chaque vue (view) est constituée des éléments suivants:
- attribut
name
: nom de la vue - attribut
style
: nom du style (attention, pour rappel ce nom ne désigne pas une stylesheet XSL, mais un style déclaré au niveau du fichier de configuration et faisant lui-même référence à une stylesheet XSL) - attribut
public
: facultatif; cet attribut permet de statuer sur l'accessibilité de la vue : une vue est considérée publique (valeurtrue
) si l'utilisateur a la possibilité de la sélectionner explicitement. À l'inverse, une vue privée (valeurfalse
) n'est pas visible de l'utilisateur. Typiquement, on qualifiera une vue enpublic="false"
s'il s'agit d'une vue activable uniquement par un script, mais non directement par l'utilisateur au niveau de l'interface - élément(s)
group
: liste des groupes qui composent la vue.- L'élément doit contenir un attribut
name
qui référence le groupe visé. - Tout autre attribut supplémentaire qui ne figure pas parmi une série
de noms réservés (
name
,label
,description
,read
,write
, etc.) est repris dans l'arbre de sortie au niveau de l'élémentgroup
.
- L'élément doit contenir un attribut
- élément(s)
section
: sous-ensemble de groupes au sein de la vue. Cet élément est facultatif. L'idée est de pouvoir structurer des groupes pour composer une section dans la page web.- Une section doit posséder un attribut
name
- Elle peut à son tour contenir des éléments
group
et/ou d'autres élémentssection
.
- Une section doit posséder un attribut
Rôle des view
¶
Les vues permettent donc de construire des écrans. Elles permettent de décrire, pour un style donné, quels groupes doivent être repris dans l'arbre de sortie. À ce titre, elles remplissent deux rôles importants:
-
Comme mentionné en introduction, les vues permettent d'indiquer quoi afficher en fonction du style actuel. Le style pouvant être forcé depuis un script, la vue permet d'indiquer quels groupes afficher en fonction du style imposé par le script. Ainsi il est possible de filtrer le contenu affiché en fonction du style.
-
Les vues remplissent un second rôle plus subtil. Elles permettent la cohabitation de plusieurs dossiers différents au sein d'un même écran. Pour rappel, Ewt permet d'afficher plusieurs dossiers simultanément. Par conséquent, il peut très bien arriver que l'on doive afficher conjointement des dossiers de modèles différents. Si chaque modèle nécessite une feuille de style spécifique, il ne serait pas possible d'afficher simultanément tous les dossiers car la feuille de style prévue pour un modèle ne serait pas capable d'afficher les données d'un autre modèle. Avec les vues, il est possible de décrire comment un dossier doit s'afficher si telle ou telle feuille de style est utilisée. Ainsi, lorsque le moteur se trouve devant le défi d'afficher simultanément plusieurs dossiers de modèles différents, il recherche les vues qui sont communes entre les différents modèles. Pour ce faire, il s'appuie sur les styles associés à ces différentes vues. Il sélectionne alors une vue qui est capable d'afficher tous les dossiers à la fois.
Clônage¶
La descript permet de spécifier le comportement du moteur concernant le clônage de dossier. Par défaut, le clônage de dossier est désactivé pour tous les modèles.
Il est possible, pour un modèle donné, d'activer le clônage. Pour ce
faire, on ajoute l'attribut cloneable
à l'élément <model>
:
1 2 3 4 |
|
La déclaration ci-dessus indique que le clônage est possible sur ce modèle.
Il est possible de déclarer des modes de clônage. Cela permet par exemple de générer une copie de dossier différente en fonction de l'utilisateur qui effectue la copie, du statut du dossier, etc. Il est possible de gérer plusieurs modes différents pour un même modèle. Pour déclarer les modes, on utilisera la syntaxe suivante:
1 2 3 4 |
|
Les valeurs true
, false
et inherit
priment sur les éventuels modes. Cela
signifie que la valeur cloneable="foo,true,bar"
est équivalente à
cloneable="true"
: les autres modes sont simplement ignorés et inclus dans
true
.
La valeur true
indique que le clônage de l'élément est possible dans tous les
cas. La valeur false
indique que le clônage de l'élément n'est pas
possible. La valeur inherit
indique que le clônage de l'élément est possible
dans les mêmes modes que pour son parent.
Lorsque le clônage est activé pour un modèle, il est valable pour tous les tuples et tous les champs du dossier, pour tous les modes déclarés. Il est cependant possible de court-circuiter cela au niveau du groupe ou du champ au moyen d'un sous-arbre du genre:
<clone>false</clone>
ou
1 2 3 4 |
|
Il est recommandé de spécifier explicitement la règle de clônage des champs qui sont des références internes de dossier (typiquement le champ qui fait le lien entre un tuple multi et le maintuple). En effet, en principe ces champs ne devraient pas être clonés, sinon cela reviendrait à rajouter un tuple au dossier source. Le moteur effectue donc un check dans ce sens et affiche un avertissement lorsqu'il rencontre ce genre de champ. Il est possible d'indiquer au moteur d'appliquer la règle du parent au moyen de la valeur:
1 |
|
Le mode entre en jeu lorsqu'on déclenche l'action
clone
: on indiquera à l'action le mode de clônage
et le moteur sera en mesure de clôner uniquement les champs qui
correspondent au mode sélectionné.
Propriétés (<properties>
)¶
Comme indiqué plus haut, les propriétés sont des valeurs personnalisées que l'on peut spécifier au niveau de la descript, des modèles, des groupes ou des champs.
Les propriétés peuvent contenir du texte ou du xml. Par défaut, la valeur passée à une propriété est reprise quasi telle quelle dans l'arbre de sortie. Il est cependant possible de demander au moteur de retraiter ladite valeur en lui spécifiant une règle de traitement sous la forme d'un namespace.
Namespaces¶
Les éléments <property>
peuvent contenir des sortes d'instructions de
traitement. Ces instructions s'expriment au moyen de namespaces que l'on
place au niveau des éléments ou de leurs attributs. Il est ainsi possible
de demander au moteur d'effectuer une traduction de libellé ou une
substitution de variable (voir ci-dessous). L'arbre de sortie reprendra
alors l'attribut ou l'élément qui était marqué d'un namespace dans l'arbre
de sortie (le namespace lui-même n'est pas repris dans l'arbre de sortie)
et la valeur de l'attribut ou de l'élément sera adaptée en fonction de la
règle attendue.
Les namespaces s'appliquent aux éléments <property>
, à leurs sous-éléments
et à leurs attributs. Il est ainsi possible de déléguer à Ewt la charge
d'adapter un libellé en fonction de la locale, de substituer une référence
de champ en fonction du contexte, etc.
Les namespaces reconnus sont:
ch.epilogic.ewt.i18n
ch.epilogic.ewt.script
ch.epilogic.ewt.text
ch.epilogic.ewt.sql
ch.epilogic.ewt.scss
ch.epilogic.ewt.instr
Les chapitres ci-dessous donnent plus d'information sur le rôle de ces namespaces.
Il est important de préciser que Ewt ne traite les namespaces sur les attributs que si l'élément ne possède pas d'autre attribut du même nom. Ainsi, pour un élément
<foo label="Salut"
i18n:label="hello.world.label"/>
le moteur n'effectuera aucun traitement de l'attribut i18n:label
car cela
engendrerait in fine un doublon d'attribut label
au niveau de l'arbre de
sortie, ce qui ne serait pas valide du point de vue xml.
Namespace ch.epilogic.ewt.i18n
¶
Ce namespace (mappé par convention sur le préfixe i18n
) fait référence à
l'internationalisation. Il permet d'indiquer au moteur qu'il doit substituer
la valeur de l'attribut ou de l'élément par la valeur correspondante dans la
langue du client.
Par exemple, un attribut i18n:label="hello.world.label"
sera
automatiquement changé en label="Salut monde"
dans le cas où le client
utilise une locale fr
et à condition évidemment que l'entrée
hello.world.label
soit définie dans le bundle de langue. Le libellé sera
adapté automatiquement en fonction de la locale.
Le namespace peut également s'appliquer à un élément. Dans ce cas, c'est
la valeur de l'élément qui doit contenir la référence de bundle. Par
exemple, l'élément <i18n:value>hello.world.label</i18n:value>
sera
remplacé par <value>Salut monde</value>
dans l'arbre de sortie.
Le document 05 - internationalisation revient également sur les aspects de namespace. Un exemple y est donné.
Exemple de requête d'extraction utilisant le namespace ch.epilogic.ewt.i18n
sur différents éléments/attributs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Namespace ch.epilogic.ewt.text
¶
Ce namespace (mappé par convention sur le préfixe text
) indique que la
valeur du paramètre est un texte pouvant contenir des références de
variables à substituer. Par exemple, un attribut
text:id="${data:idVendeur}"
apparaîtra comme id="12"
dans l'arbre de
sortie dans le cas où le champ idVendeur
du dossier courant contient la
valeur "12".
Namespace ch.epilogic.ewt.sql
¶
Ce namespace (mappé par convention sur le préfixe sql
) indique que la
valeur du paramètre est une requête SQL, éventuellement accompagnée de
paramètres. Le moteur se chargera alors d'évaluer la requête et de reprendre
le résultat en valeur dans l'arbre de sortie.
Dans la version actuelle du moteur, le namespace n'est pris en charge que pour le traitement des éléments XML. Le fait d'appliquer le namespace à un attribut est sans effet (la requête sera reprise telle quelle dans l'arbre de sortie).
L'élément XML qui est défini avec ce namespace doit respecter une certaine
forme. Il s'agit en réalité de la même forme que celle utilisée dans le cas
des requêtes SQL pour les valeurs par défaut de champ ou pour les listes
d'options: l'élément doit posséder un sous-élément <value>
dans lequel la
requête (ou plutôt le statement) est donnée. Il est également possible de
définir des sous-éléments <param>
qui seront les valeurs à passer au
prepared statement. Ces paramètres doivent être typés (attribut type
) et
peuvent être nommés (attribut name
) dans le cas où la requête fait des
références de paramètres nommés. Voici un exemple:
1 2 3 4 5 6 7 8 9 |
|
Dans l'exemple ci-dessus, on utilise la référence ?
pour représenter une
donnée. Les paramètres ne doivent donc pas être nommés. Ils doivent par
contre être typés. Ici on spécifie un paramètre avec le type auto
. Ce
type peut être utilisé pour les identifiants de tuples. Le moteur détermine
alors le type en fonction du type des primary key spécifié dans la config de
l'application.
Le même exemple peut être adapté pour utiliser des paramètres nommés (seules les lignes 5 et 6 sont modifiées par rapport à l'exemple précédent):
1 2 3 4 5 6 7 8 9 |
|
Le moteur reconnaît les attributs format
, sanitize
, header
, escape
et attributes
définis sur l'élément auquel le namespace est déclaré
(élément extraction
dans l'exemple ci-dessus). Ils influent sur la forme
de la valeur générée en sortie. Ils jouent le même rôle que dans les options
de la méthode $sql.mselect
. L'attribut
format
peut prendre les mêmes valeurs que pour la méthode $sql.mselect
,
à la différence qu'ici la valeur auto
correspond à la valeur xml
.
L'attribut sanitize
peut être spécifié sous forme de texte ou de json.
L'attribut attributes
doit être spécifié sous forme de json, comme c'est
le cas dans l'exemple ci-dessus. Notez au passage l'utilisation de
délimiteur '
(apostrophe) pour pouvoir spécifier du json valide. On
aurait également pu noter l'attribut
attributes="{ "class": "search-result" }"
, mais cela
aurait été moins lisible.
Les attributs format
, sanitize
, header
, escape
et attributes
peuvent également tous être spécifiés en tant qu'éléments au même niveau que
<value>
et <param>
. Par contre le moteur ne s'attend pas à trouver ces
éléments s'ils sont déjà définis en tant qu'attributs.
1 2 3 4 5 6 7 8 9 10 |
|
Dans le cas des formats xml
, html
ou xhtml
, il est possible d'indiquer
au moteur sous quelle forme le résultat doit être fourni. Par défaut, il est
retourné sous forme d'XML directement dans l'arbre de sortie. Il est
cependant possible d'ajouter un attribut ou un élément output
avec la
valeur text
pour demander au moteur de retourner le résultat sous forme de
texte.
Utilisation de caractères spéciaux
Le fichier de descript étant un fichier XML, la requête SQL doit
respecter la syntaxe XML et les caractères spéciaux doivent être
échappés. Ainsi, un test where somefield < 10
devra être noté
somefield < 10
.
Pour éviter cette syntaxe peut lisible, nous recommandons d'inscrire les requêtes SQL dans un bloc CDATA. La requête pourra donc être notée:
<value><![CDATA[select ... where somefield < 10]]></value>
Si vous devez écrire une requête sql qui utilise des caractères spéciaux
Namespace ch.epilogic.ewt.script
¶
Ce namespace (mappé par convention sur le préfixe script
) indique que la
valeur de l'élément ou du paramètre est un script ou une référence de script
à évaluer. Par exemple, un attribut script:label="return 1+2;"
apparaîtra
comme label="3"
dans l'arbre de sortie.
Le namespace peut être utilisé sur les attributs (comme ci-dessus) ou sur
les éléments. Dans le cas des attributs, le script ne peut prendre aucun
paramètre. Dans le cas des éléments, le comportement s'apparente au cas du
namespace ch.epilogic.ewt.sql
, à savoir que:
- Le script ou la référence de script doit être passée via un élément
<value>
. La valeur peut donc être un script inline ou une référence de script de l'application. - Il est possible de spécifier 0 ou n paramètres via des éléments
param
. Ces derniers DOIVENT avoir un attributname
qui donne le nom du paramètre que recevra le script. Un attributtype
permet de spécifier le type du paramètre. Attention, ici le type doit désigner le type d'objet. Les types autorisés sontstring
,number
,date
,time
,timestamp
,map
,array
,file
ounull
. Dans le cas des typesmap
etarray
, le moteur s'attend à recevoir une version json de l'objet.
Dans l'exemple ci-dessous, on définit un script inline qui extrait la
propriété foo
d'un map défini en json.
1 2 3 4 |
|
Par défaut la valeur de retour inscrite dans l'arbre de sortie est du texte
simple. Il est possible d'ajouter un attribut output
à l'élément racine
pour indiquer au moteur sous quelle forme la valeur doit être inscrite dans
l'arbre de sortie. Lorsque cet attribut vaut xml
, le moteur force la
valeur à être inscrite sous la forme d'élément XML (pour autant que la
valeur générée par le script s'y prête).
1 2 3 |
|
Namespace ch.epilogic.ewt.scss
¶
Ce namespace (mappé par convention sur le préfixe scss
) indique que la
valeur du paramètre est du SCSS qui doit être compilé en CSS. L'arbre de
sortie affichera donc la valeur compilée. La compilation est effectuée au
chargement de la descript. Cela évite de la refaire au runtime à chaque
génération d'écran.
Exemple de propriété utilisant le namespace ch.epilogic.ewt.scss
pour
compiler une feuille de style CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Namespace ch.epilogic.ewt.instr
¶
Ce namespace (mappé par convention sur le préfixe instr
) désigne des
instructions de traitement passées au moteur. Ces instructions ne sont
pas reprises dans l'arbre de sortie mais influent sur la façon de
structurer son contenu. Elles sont donc réservées au moteur et permettent
d'indiquer à ce dernier comment il doit traiter la propriété.
La version actuelle du moteur prévoit l'instruction suivante :
-
attribut
destination
: indique dans quelle(s) branche(s) de l'arbre de sortie l'élément doit être repris; la valeur peut être l'une de valeurs suivantes, ou une combinaison de plusieurs valeurs séparées par une virgule:descript-base
: La propriété doit être reprise au niveau du sous-arbre<descript>
de l'arbre de sortie, uniquement lorsque celui-ci affiche un résumé du modèle (sans les groupes et les tuples)descript-full
: La propriété doit être reprise au niveau du sous-arbre<descript>
de l'arbre de sortie, uniquement lorsque celui-ci est détaillé, c'est-à-dire lorsqu'un dossier du modèle est ouvert.descript
: cette propriété correspond à la combinaisondescript-base,descript-full
; la propriété doit donc être reprise au niveau du sous-arbre<descript>
de l'arbre de sortie, quelle que soit la forme (résumé ou détaillé).document
: la propriété doit être reprise dans l'élément correspondant à celui dans lequel elle est déclarée, mais au niveau du sous-arbre<document>
de l'arbre de sortie. Par exemple, une propriété déclarée dans un élément<field>
de la descript sera reprise au niveau de l'élément<field>
du sous-arbre<document>
de l'arbre de sortieheader
: la propriété doit être reprise au niveau du sous-arbre<header>
de l'arbre de sortie (uniquement dans le cas de groupes multi)
Il est possible, comme dans l'exemple ci-dessous, de spécifier plusieurs destinations en les séparant par une virgule. Ici l'instruction
destination
demande au moteur de reprendre le nœudproperty
au niveau des documents et des headers.1 2 3 4 5
<property name="extractions" instr:destination="document,header"> <extraction position="after"> ... </extraction> </property>
En l'absence d'instruction
destination
, le moteur détermine une valeur par défaut en fonction de l'élément auquel il est appliqué et au contexte dans lequel cet élément est intégré. Ainsi, une propriété définie au niveau racine de la descript aura une destination par défaut correspondant àdescript
, alors qu'une propriété définie au niveau d'un modèle, d'un groupe ou d'un champ aura une destination par défaut équivalente à la combinaisondescript-full,document
.
⚠️ Il convient de mentionner que certaines substitutions (notamment les
références à des valeurs de dossiers) ne pourront se faire que dans le cas
où la destination inclut document
. En effet, le moteur ne pourra résoudre
une expression du genre #projet.name
que dans le contexte d'un dossier.