Quick Start

Document User Interface 1.0

Anakeen Labs <labs@anakeen.com>

Table des matières

Chapitre 1 Introduction

1.1 Présentation et objectif

Ce document est le tutoriel du module Dynacase Document UIs.

Anakeen propose des prestations de formation à l'utilisation de la plateforme Dynacase.
Ces formations, d'une durée de 5 jours en standard, permettent d'aborder les points de ce tutoriel.
Elles peuvent aussi être adaptées pour aborder des thèmes non présentés dans ce document. Elles alternent parties théoriques et applications pratiques. Les parties pratiques s'appuient sur un projet standard ou peuvent être basées sur votre propre projet.
Elles se déroulent en intra entreprise sur Paris ou Toulouse.

1.2 Organisation

Après la mise en place de l'environnement de travail, ce tutoriel est organisé en 5 chapitres principaux :

Chaque partie (et ses sous-parties) s'organisent de la même manière :

1.3 Suivi du tutoriel

Vous pouvez suivre ce tutoriel de 2 manières différentes :

Au terme de ce tutoriel, vous connaîtrez les principales fonctionnalités de Dynacase Document UIs.

Vous serez également en mesure d'explorer plus en détails les possibilités offertes par Dynacase Document UIs, et de réaliser des applications HTML5 tirant parti des derniers standards du web.

Chapitre 2 Mise en place de l'environnement de travail

2.1 Environnement d'exécution

Pour dérouler ce tutoriel, il est nécessaire d'avoir un contexte dynacase.

Ce contexte peut être obtenu de 3 manières différentes.

2.1.1 Virtualbox

Une appliance virtualbox est disponible sur notre site : dynacase-quick-start-ddui.ova. Il suffit de l'importer et de la démarrer.

L'appliance mappe le port 80 de l'invité sur le port 8080 de l'hôte.

2.1.2 Autre

Suivre le manuel d'installation de Dynacase pour créer un environnement de votre choix.

2.2 Environnement de développement

Pour suivre ce tutoriel, il vous faudra

pour plus de détails sur le developer toolkit, se reporter au manuel de référence

2.3 Accès aux sources

Les sources de ce tutoriel sont sur github.

Au cours du tutoriel, les fichiers modifiés pourront être téléchargés directement depuis github via un lien.

De plus, chaque étape est identifiée par un tag. Vous pouvez accéder à ce tag au moyen de git si vous avez cloné le dépot, ou vous pouvez télécharger directement une archive pour chaque étape depuis github via un lien.

Chapitre 3 Découverte de Dynacase Document UIs

3.1 Qu'est-ce que Dynacase Document UIs

Dynacase Document UIs est un module Dynacase permettant de générer une représensation des documents au moyen des technologies HTML5.

Le rendu des documents sans ce module est fait par le serveur, sous la forme d'une page HTML4 monolithique. Dynacase Document UIs permet à la génération de se faire côté client au moyen de Javascript, HTML5 et CSS.

Cela apporte les avantages suivants :

Pour une présentation plus détaillée de Dynacase Document UIs, se reporter au manuel de référence

3.2 L'application de gestion des contacts

Au cours de ce tutoriel, nous allons construire une application de gestion des contacts.

Application de gestion des contacts

Figure 2. Application de gestion des contacts

Cette application sera composée des éléments Dynacase suivants :

3.3 Mise en œuvre de Dynacase Document UIs

3.3.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-20-00.

3.3.2 Code

L'étape de départ de ce tutoriel contient déjà la famille (avec son workflow), ainsi que le contact John Doe. Dans un premier temps, nous allons travailler directement sur le document.

Il suffit donc de déployer les sources.

3.3.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-20-00

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation) :

3.3.4 Le résultat

Il est possible de consulter le document

Pour plus d'explications sur l'application DOCUMENT, voir le manuel de référence ddui

3.4 Comparaisons entre la représentation traditionnelle et la représentation HTML5

Comparaison des rendus

Figure 3. Comparaison des rendus

Si vous connaissiez Dynacase avant la sortie de Dynacase Document UIs, voici les éléments clés différenciant la représentation traditionnelle et la représentation HTML5.

Au-delà de ces aspects visuels, chaque attribut a été construit de manière à ce que ses éléments soient facilement adressables par CSS, facilement surchargeables au moyen de JavaScript. Nous allons exploiter cette nouvelle souplesse dans les chapitres suivants.

Chapitre 4 Design adaptif (responsive) avec Dynacase Document UIs

Nous avons vu dans le chapitre précédent que le document est responsive par défaut : en dessous de 480px, les libellés passent au dessus des attributs. Nous allons voir dans ce chapitre comment aller plus loin.

4.1 Principe

Nous allons injecter une CSS qui change la mise en forme du document, afin de le rendre responsive. L'objet de ce chapitre n'est pas d'expliquer la CSS, mais de montrer comment on peut

Voici à quoi ressemblera le document final :

Fiche de contact

Figure 4. Fiche de contact

4.2 Mise en œuvre du design responsive

4.2.1 Mise en place du contrôle de rendu

Nous allons créer un contrôle de rendu afin de surcharger le rendu du document. Pour rappel, le contrôle de rendu se manipule de la même manière que le contrôle de vue :

4.2.1.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-30-00.

4.2.1.2 Paramétrage

Le contrôle de rendu fera référence à une classe de rendu. Cette classe de configuration rendu doit être initialisée avant création du contrôle de rendu. Elle sera utilisée pour modifier le rendu du document dans les étapes suivantes.

Voici le contenu du fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactRenderConfigView.php :

<?php
 
namespace DduiTuto;
 
class ContactRenderConfigView extends \Dcp\Ui\DefaultView
{
 
}

Une fois cette classe déployée, le contrôle de rendu peut être créé à cette adresse : http://localhost:8080/?app=GENERIC&action=GENERIC_EDIT&classid=CVRENDER

Nous allons le compléter avec les informations suivantes :

Contrôle de rendu

Figure 5. Contrôle de rendu

Une fois ajouté dans le workflow de la famille Contact pour chacun des états, exporté, et correctement ajouté au fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/DDUI_TUTO_CONTACT__DATA.csv, il peut être déployé.

4.2.1.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-30-10

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation) :

Puisque le contrôle de rendu a été associé au cycle de vie après la création du document que nous manipulons, il n'est pas associé automatiquement. Il faudrait soit écrire un script de migration, soit créer un nouveau document. Pour plus de simplicité, nous avons intégré une méthode à notre document pour refaire l'association.

il suffit de se connecter à l'adresse http://localhost:8080/?app=FDL&action=FDL_METHOD&id=CONTACT_JOHN_DOE&method=updateCR

4.2.1.4 Le résultat

En consultant le contact John DOE à l'adresse http://localhost:8080/api/v1/documents/CONTACT_JOHN_DOE.html, aucun changement n'est perceptible. En effet, nous avons juste mis en place les points d'entrée pour nos personnalisation à venir.

4.2.2 CSS uniquement

Nous allons dans cette partie voir comment surcharger le rendu uniquement par CSS.

L'objectif est de

Soit, en images :

Résolution inférieure à 480px

Figure 6. Résolution inférieure à 480px

Résolution entre 480px et 1024px

Figure 7. Résolution entre 480px et 1024px

Résolution entre 1024px et 1280px

Figure 8. Résolution entre 1024px et 1280px

Résolution supérieure à 1280px

Figure 9. Résolution supérieure à 1280px

4.2.2.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-30-10.

4.2.2.2 Code

Nous devons en premier lieu injecter la nouvelle CSS dans le document en plus des CSS qu'il utilise déjà. Pour ce faire, ajoutons une méthode getCssReferences à notre classe de configuration de rendu dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactRenderConfigView.php :

public function getCssReferences(\Doc $document = null)
{
    $version = \ApplicationParameterManager::getParameterValue(
        "CORE", "WVERSION"
    );
 
    $cssReferences = parent::getCssReferences($document);
 
    $cssReferences['DDUI_TUTO_CONTACT_view']
        = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/view.css?ws="
        . $version;
 
    return $cssReferences;
}

Nous pouvons maintenant écrire la CSS correspondante dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/view.css :

/******************************************************************************
 cadre identité
******************************************************************************/
 
/* masquage du label de la photo */
label[data-attrid="dc_photo"] {
    display: none;
}
 
@media (min-width: 480px) {
 
    /* Positionnement de la photo à gauche */
    .dcpAttribute[data-attrid="dc_photo"] {
        float: left;
        width: 66px;
    }
 
    .dcpAttribute__content[data-attrid="dc_photo"] {
        padding: 0;
    }
 
    /* Restauration de la position initiale de tous les attributs autres que la photo */
    .dcpFrame__content[data-attrid=dc_fr_ident] .dcpAttribute:not([data-attrid="dc_photo"]) {
        float: right;
        width: calc(100% - 66px);
    }
 
    .dcpFrame__content[data-attrid=dc_fr_ident] label:not([data-attrid="dc_photo"]) {
        width: calc((100% / 3) - 44px);
    }
}
 
/******************************************************************************
 cadre Société
******************************************************************************/
 
@media (max-width: 1024px),
       (min-width: 1280px) {
 
    /* masquage du label du logo */
    label[data-attrid="dc_logo"] {
        display: none;
    }
}
 
@media (min-width:480px) and (max-width:1024px),
       (min-width: 1280px) {
 
    /* Positionnement du logo à gauche */
    .dcpAttribute[data-attrid="dc_logo"] {
        float: left;
        width: 66px;
    }
 
    .dcpAttribute__content[data-attrid="dc_logo"] {
        padding: 0;
    }
 
    /* Restauration de la position initiale de tous les attributs autres que le logo */
    .dcpFrame__content[data-attrid=dc_fr_society] .dcpAttribute:not([data-attrid="dc_logo"]) {
        float: right;
        width: calc(100% - 66px);
    }
 
    .dcpFrame__content[data-attrid=dc_fr_society] label:not([data-attrid="dc_logo"]) {
        width: calc((100% / 3) - 44px);
    }
}
 
/******************************************************************************
 Positionnement côte à côte des cadres "Société" et "Coordonnées professionnelles"
******************************************************************************/
@media (min-width: 1024px) {
    .dcpFrame[data-attrid=dc_fr_society],
    .dcpFrame[data-attrid=dc_fr_coord] {
        float: right;
        width: calc(50% - 10px);
    }
 
    .dcpFrame[data-attrid=dc_fr_society] {
        float: left;
    }
 
    .dcpFrame[data-attrid=dc_fr_coord] {
        float: right;
    }
}

4.2.2.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-30-20

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation) :

4.2.2.4 Le résultat

En consultant le contact John DOE à l'adresse http://localhost:8080/api/v1/documents/CONTACT_JOHN_DOE.html, on constate bien que les éléments sont réorganisés en fonction de la résolution.

4.2.3 Utilisation des templates

Dans la partie précédente, nous avons changé la mise en page uniquement au moyen de CSS. Toutefois, il peut être nécessaire d'aller plus loin et de revoir entièrement la disposition des éléments. Dans ce cas, la CSS n'est plus suffisante, et il faut alors passer par des templates.

Il est possible de définir un template pour un attribut en particulier, ou tout ou partie du document (voir le manuel de référence pour plus d'explications sur les templates).

Nous allons définir un template qui surcharge l'intégralité du corps du document pour arriver à ce résultat :

Fiche de contact

Figure 10. Fiche de contact

4.2.3.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-30-20.

4.2.3.2 Code

La première étape est d'indiquer le template à utiliser dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactRenderConfigView.php. Cela se fait au moyen de la méthode getTemplates. Nous allons également injecter 2 fichiers javascript, qui seront utilisés par la suite, au moyen de la méthode getJsReferences. Voici le fichier final :

<?php
 
namespace DduiTuto;
 
class ContactRenderConfigView extends \Dcp\Ui\DefaultView
{
    public function getTemplates(\Doc $document = null)
    {
        $templates = parent::getTemplates($document);
 
        $templates["sections"]["content"]["file"]
            = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/contactContent.mustache";
        return $templates;
    }
 
    public function getJsReferences(\Doc $document = null)
    {
        $version = \ApplicationParameterManager::getParameterValue(
            "CORE", "WVERSION"
        );
 
        $jsReferences = parent::getJsReferences();
 
        $jsReferences["bootstrap_collapse"]
            = "lib/bootstrap/3/js/collapse.js?ws="
            . $version;
 
        $jsReferences["jsqr"]
            = "lib/jsqr/jsqr-1.0.2-min.js?ws="
            . $version;
 
        return $jsReferences;
    }
 
    public function getCssReferences(\Doc $document = null)
    {
        $version = \ApplicationParameterManager::getParameterValue(
            "CORE", "WVERSION"
        );
 
        $cssReferences = parent::getCssReferences($document);
 
        $cssReferences['DDUI_TUTO_CONTACT_view']
            = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/view.css?ws="
            . $version;
 
        return $cssReferences;
    }
}

Nous pouvons maintenant définir notre template. Ce template utilisera des variables mustache pour récupérer les valeurs du document. Ces variables sont présentées dans le manuel de référence.

Le template ainsi obtenu dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/contactContent.mustache est le suivant :

<div class="container dc__main">
    <div class="well clearfix">
        <div class="dc__summary">
            <div class="media">
                <div class="media-left media-middle">
                    <img src="{{document.attributes.dc_photo.attributeValue.url}}&width=150"
                         alt="Pas de photo de contact">
                </div>
 
                <div class="media-body">
                    <div class="media">
                        <div class="media-body">
                            <h1 class="media-heading pull-right">
                                {{document.attributes.dc_society.attributeValue.displayValue}}
                            </h1>
                        </div>
                        <div class="media-right media-middle">
                            <img src="{{{document.attributes.dc_logo.attributeValue.thumbnail}}}"
                                 alt="pas de logo de la société">
                        </div>
                    </div>
                    {{{document.attributes.dc_civility.attributeValue.displayValue}}}
                    <h2 class="media-heading">
                        {{{document.attributes.dc_firstname.attributeValue.displayValue}}}
                        {{{document.attributes.dc_lastname.attributeValue.displayValue}}}
                    </h2>
 
                    {{{document.attributes.dc_service.attributeValue.displayValue}}}&nbsp;({{{document.attributes.dc_role.attributeValue.displayValue}}})
                </div>
            </div>
        </div>
        <div id="qrcontainer" class="dc__qrCode"></div>
    </div>
 
    <div class="panel panel-default">
        <div class="panel-heading" role="tab" id="headingMore">
            <h3 class="panel-title">
                <a class="collapsed" role="button" data-toggle="collapse" href="#collapseMore"
                   aria-expanded="true" aria-controls="collapseOne">
                    <i class="collapseMore__icon fa" /> En savoir plus
                </a>
            </h3>
        </div>
        <div id="collapseMore" class="panel-collapse collapse"
             role="tabpanel" aria-labelledby="headingMore">
            <div class="panel-body">
                <div class="row dc__coord">
                    <div class="col-sm-4">
                        <i class="fa fa-phone"></i> {{{document.attributes.dc_workphone.htmlContent}}}
                    </div>
                    <div class="col-sm-4">
                        <i class="fa fa-mobile"></i> {{{document.attributes.dc_mobilephone.htmlContent}}}
                    </div>
                    <div class="col-sm-4">
                        <i class="fa fa-envelope"></i> {{{document.attributes.dc_workmail.htmlContent}}}
                    </div>
                </div>
                <div class="row">
                    <div class="col-sm-6 ">
                        {{{document.attributes.dc_fr_workaddr.htmlView}}}
                    </div>
                    <div class="col-sm-6 ">
                        {{{document.attributes.dc_fr_homeaddr.htmlView}}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

En bonus dans la version corrigée, vous avez accès au code permettant de générer le QRcode.

Enfin, ce template s'accompagne de CSS. Nous allons donc réécrire le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/view.css :

.dc__main {
    font-size: 16px;
    padding-top: 20px;
}
 
.dc__coord {
    text-align: center;
}
 
.dc__coord .dcpCustomTemplate--content {
    display: inline-block;
}
 
.dc__coord .fa {
    font-size: 150%;
    vertical-align: top;
    padding-top: 0.2em;
    width: 2em;
    text-align: center;
}
 
.dcpAttribute[data-attrid="dc_workpostalcode"],
.dcpAttribute[data-attrid="dc_workcity"],
.dcpAttribute[data-attrid="dc_homepostalcode"],
.dcpAttribute[data-attrid="dc_homecity"] {
    float: left;
    display: inline-block;
    margin-right: 2px;
}
 
.dcpAttribute__content[data-attrid="dc_workpostalcode"],
.dcpAttribute__content[data-attrid="dc_workcity"],
.dcpAttribute__content[data-attrid="dc_homepostalcode"],
.dcpAttribute__content[data-attrid="dc_homecity"] {
    width: auto;
}
 
.dcpAttribute__label {
    display:none;
}
 
.panel-heading[data-id="dc_fr_workaddr"], /* because of http://dev.dynacase.org/issues/6107 */
.panel-heading[data-attrid="dc_fr_workaddr"],
.panel-heading[data-id="dc_fr_homeaddr"], /* because of http://dev.dynacase.org/issues/6107 */
.panel-heading[data-attrid="dc_fr_homeaddr"] {
    font-size: 14px;
}
 
.collapseMore__icon::before {
    content: "\f0da"; /* fa-caret-right */
}
 
a:not(.collapsed) .collapseMore__icon::before {
    content: "\f0d7"; /* fa-caret-down */
}
 
.panel-title a:hover {
    text-decoration: none;
}
 
.dc__qrCode {
    margin-top: 10px;
    text-align: center;
}
 
@media (min-width:768px) {
    .dc__summary,
    .dc__qrCode {
        float: left;
    }
    .dc__summary {
        width: calc(100% - 210px);
        padding-right: 10px;
    }
    .dc__qrCode {
        width: 210px;
        text-align: right;
        margin-top: 0;
    }
}

4.2.3.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-30-30

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation) :

4.2.3.4 Le résultat

En consultant le contact John DOE à l'adresse http://localhost:8080/api/v1/documents/CONTACT_JOHN_DOE.html, la page ressemble à ce qui était attendu. De plus, le redimensionnement de la page réorganise bien les éléments.

4.3 Conclusion

Vous avez vu dans cette partie qu'il est simple de changer la disposition des éléments d'un document au moyen de CSS, que chaque attribut, ou même chaque sous-élément d'un attribut (libellé, valeur, etc.) est facilement adressable en CSS, et que vous pouvez aussi définir des templates pour des mises en page plus complexes.

De plus, tout ce travail a été accompli en n'écrivant aucun autre php que le code nécessaire pour définir notre template, et ajouter les assets au document.

Dans la partie suivante, nous allons voir comment travailler avec du code Javascript et php pour personnaliser le comportement des documents.

Chapitre 5 Conception d'un assistant (wizard) avec Dynacase Document UIs

5.1 Principe

Le wizard permet de découper la saisie du document en plusieurs étapes. Chaque étape définit un ensemble de conditions pour pouvoir passer à l'étape suivante.

Chaque étape est implémentée au moyen d'une vue, accompagnée d'un masque. Ces vues sont pilotées au moyen d'un contrôle de rendu, et d'une classe d'accès à un rendu.

La dernière étape atteinte est mémorisée dans un paramètre applicatif du document.

La partie serveur sélectionne la vue en se basant sur les informations envoyées par le client au moyen des customClientData.

Pendant le wizard, le document est à l'état Création.

À la fin du wizard, le document est passé à l'état À jour.

L'état Création n'est plus accessible après achèvement du wizard.

Voici à quoi ressemble le document pendant le wizard :

Wizard

Figure 11. Wizard

Les étapes sont les suivantes :

5.2 Mise en œuvre du wizard

5.2.1 Mise en place des vues

3 vues vont être ajoutées sur le contrôle de rendu. Le contrôle de rendu va également préciser une classe d'accès à un rendu, qui va déterminer la vue suivante en fonction de :

5.2.1.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-00.

5.2.1.2 Code

Chaque vue est composée de :

De plus, le contrôle de rendu est associé à une classe d'accès à un rendu.

Ces éléments doivent donc être initialisés avant création du contrôle de rendu.

Nous allons donc commencer par initialiser notre classe de rendu, commune aux 3 nouvelles vues :

DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

<?php
 
namespace DduiTuto;
 
class ContactWizardRenderConfigEdit extends \Dcp\Ui\DefaultEdit
{
 
}

Nous allons ensuite initialiser la classe d'accès à un rendu dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactCvrenderRenderConfigAccess.php :

<?php
 
namespace DduiTuto;
 
use Dcp\Ui\IRenderConfig;
use Dcp\Ui\IRenderConfigAccess;
 
class ContactCvrenderRenderConfigAccess implements IRenderConfigAccess
{
    /**
     * @param string $mode
     *
     * @return IRenderConfig
     */
    public function getRenderConfig($mode, \Doc $document)
    {
        return null;
    }
}

Nous allons également initialiser les 3 masques qui seront utilisés par chacune des vues. Chaque masque peut être initialisé à l'adresse suivante : http://localhost:8080/?app=GENERIC&action=GENERIC_EDIT&classid=MASK&msk_famid=DDUI_TUTO_CONTACT

Une fois exportés, et correctement ajoutés au fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/DDUI_TUTO_CONTACT__DATA.csv, il peuvent être déployés.

Il est maintenant possible de rajouter nos 3 vues dans le contrôle de rendu, à l'adresse http://localhost:8080/?app=FDL&action=OPENDOC&mode=view&id=DDUI_TUTO_CONTACT__CVRENDER. Les vues sont donc maintenant au nombre de 4 :

Contrôle de rendu

Figure 12. Contrôle de rendu

Une fois exporté, et correctement ajouté au fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/DDUI_TUTO_CONTACT__DATA.csv, il peut être déployé.

5.2.1.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-10

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.1.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que seul le cadre identité est présenté. En effet, notre classe d'accès à un rendu retourne null, laissant le contrôle de rendu afficher sa vue par défaut, c'est à dire la première vue de modification.

5.2.2 Partie serveur : choix de la vue

La partie serveur sélectionnera la vue en se basant sur les informations suivantes, envoyées par le client au moyen des customClientData :

currentWizardStepName

le nom de l'étape en cours

Si ce nom est vide, alors on sélectionne la dernière vue atteinte (mémorisée au moyen d'un paramètre applicatif), et la première vue si le paramètre applicatif n'est pas renseigné.

goto

Une consigne sur l'étape à atteindre, parmi :

wizard.previous
Indique que l'utilisateur veut revenir vers l'étape précédente.
wizard.next
Indique que l'utilisateur veut aller vers l'étape suivante.
wizard.targetStep

Indique que l'utilisateur veut accéder à une étape spécifique.

Dans ce cas, l'étape en question est passée par le paramètre targetStep.

5.2.2.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-10.

5.2.2.2 Code

Le code de sélection de la vue en fonction des vues disponibles dans le contrôle de rendu, et des consignes envoyées par le client est fourni au moyen de la méthode initWizardInfos depuis la trait TContactWizardRenderConfigEdit.

Il suffit de charger cette trait dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php.

La méthode initWizardInfos est une manière d'implémenter un wizard. Sa complexité n'a aucun intérêt particulier pour le compréhension du tutoriel, et elle n'est fournie qu'à titre d'exemple.

<?php
 
namespace DduiTuto;
 
use Dcp\AttributeIdentifiers\cvrender as CvrenderAttributes;
use Dcp\Family\Cvrender as CvrenderFamily;
 
class ContactWizardRenderConfigEdit extends \Dcp\Ui\DefaultEdit
{
    use TContactWizardRenderConfigEdit;
}

Nous pouvons maintenant définir cette configuration de rendu comme étant la configuration de rendu à utiliser pour les vues de création et de modification. Cela se fait dans la classe ContactCvrenderRenderConfigAccess du fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactCvrenderRenderConfigAccess.php :

<?php
 
namespace DduiTuto;
 
use Dcp\Ui\IRenderConfig;
use Dcp\Ui\IRenderConfigAccess;
use Dcp\Ui\RenderConfigManager;
 
class ContactCvrenderRenderConfigAccess implements IRenderConfigAccess
{
    /**
     * @param string $mode
     * @param \Doc   $document
     *
     * @return IRenderConfig
     */
    public function getRenderConfig($mode, \Doc $document)
    {
        switch($mode) {
        case RenderConfigManager::ViewMode:
            //let the CV RENDER do the job
            return null;
        default:
            $wizardRenderConfig = new ContactWizardRenderConfigEdit();
            $wizardRenderConfig->initWizardInfos($document);
            return $wizardRenderConfig;
        }
    }
}

5.2.2.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-20

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.2.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que seul le cadre identité est présenté. En effet, en l'absence de données du client, la première vue est sélectionnée et appliquée.

5.2.3 Partie serveur : mise en place du header

Le menu est surchargé au moyen d'un template personnalisé pour afficher les différentes étapes du wizard au dessus du menu.

5.2.3.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-20.

5.2.3.2 Code

Le template utilisé est le suivant, à enregistrer dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/contactWizardHeader.mustache :

[[#wizardInfos]]<div class="wizard_summary clearfix">[[#steps]]
    <div class="wizard_summary__step [[#current]]wizard_summary__step_current[[/current]]"
         data-viewid="[[cv_idview]]"
         style="width:calc(100% / [[nbSteps]])">
        <div class="wizard_summary__step__label [[#current]]wizard_summary__step__label_current[[/current]]"
             title="[[#i18n]]ddui_tuto:wizard::goto step[[/i18n]] [[cv_lview]]"
             data-viewid="[[cv_idview]]">[[cv_lview]]
        </div>
        <div class="wizard_summary__step__attributes">[[#attributes]][[#fields]]
            <div class="wizard_summary__step__attribute [[#required]]wizard_summary__step__attribute_required[[/required]] [[#filled]]wizard_summary__step__attribute_filled[[/filled]]"
                 data-viewid="[[cv_idview]]"
                 data-attrid="[[attrid]]"
                 data-attrlabel="[[label]]"
                 style="width: calc(100% / [[nbFields]])"
                 title="[[label]]">
            </div>
            [[/fields]][[/attributes]]
        </div>
    </div>
[[/steps]]</div>[[/wizardInfos]]
<nav class="dcpDocument__menu"></nav>

La css correspondante, à enregistrer dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.css, est la suivante :

.wizard_summary__step {
    float: left;
    padding: 2px 0px;
    text-align: center;
    cursor: pointer;
}
 
.wizard_summary__step::before {
    content: "\f054"; /* fa-chevron-right */
    font-size: 2em;
    font-family: FontAwesome;
    font-style: normal;
    font-weight: normal;
    float: right;
    vertical-align: middle;
    width: 30px;
    text-align: center;
    color: green;
}
 
.wizard_summary__step:first-child {
    padding-left: 5px;
}
 
.wizard_summary__step:last-child {
    padding-right: 5px;
}
 
.wizard_summary__step:last-child::before {
    content: "\f024"; /* fa-flag */
}
 
.wizard_summary__step_optional_unfilled::before {
    color: orange;
}
 
.wizard_summary__step_required_unfilled::before {
    color: red;
}
 
.wizard_summary__step:hover,
.wizard_summary__step:active {
    font-style: italic;
    text-decoration: underline;
}
 
.wizard_summary__step_current {
    background-color: WhiteSmoke;
}
 
.wizard_summary__step__label {
    font-weight: bold;
    font-size: 120%;
    padding: 3px;
}
 
.wizard_summary__step__label_current {
    font-style: italic;
}
 
.wizard_summary__step__attributes {
    width: calc(100% - 30px);
    float: left;
}
 
.wizard_summary__step__attribute {
    height: 3px;
    background-clip: content-box;
    float: left;
    padding: 0 2px;
    background-color: orange;
}
 
.wizard_summary__step__attribute_required {
    background-color: red;
}
 
.wizard_summary__step__attribute_filled {
    background-color: green;
}

Enfin, 3 méthodes sont surchargées dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

getCssReferences
injecte le fichier css précédemment créé.
getTemplates
remplace le menu par le template précédemment créé.
getContextController
injecte les propriétés de wizardInfos dans le moteur de template pour compléter le bandeau de navigation.

Soit :

public function getContextController(\Doc $document)
{
    $this->initWizardInfos($document);
 
    $controller = parent::getContextController($document);
    $controller["wizardInfos"] = $this->wizardInfos;
    return $controller;
}
 
public function getTemplates(\Doc $document = null)
{
    $this->initWizardInfos($document);
 
    $templates = parent::getTemplates($document);
 
    $templates["sections"]["menu"]["file"]
        = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/contactWizardHeader.mustache";
 
    return $templates;
}
 
public function getCssReferences(\Doc $document = null)
{
    $version = \ApplicationParameterManager::getParameterValue(
        "CORE", "WVERSION"
    );
 
    $cssReferences = parent::getCssReferences($document);
 
    $cssReferences['DDUI_TUTO_CONTACT_WIZARD']
        = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.css?ws="
        . $version;
 
    return $cssReferences;
}

5.2.3.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-30

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.3.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que le bandeau de navigation entre les différentes étapes est maintenant visible.

Ce bandeau n'est pas fonctionnel pour le moment. Les chapitres suivants feront petit à fonctionner chacun des éléments de ce bandeau.

5.2.4 Partie serveur : Personnalisation du menu

Les entrées de menu vont être surchargées :

5.2.4.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-30.

5.2.4.2 Code

La méthode getMenu est surchargée dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

public function getMenu(\Doc $document)
{
    $this->initWizardInfos($document);
 
    $menu = parent::getMenu($document);
 
    //Hide some menus
    foreach(['save', 'create', 'createAndClose', 'close', 'workflow'] as $elementId) {
        $element = $menu->getElement($elementId);
        if (!is_null($element)) {
            $element->setVisibility(ElementMenu::VisibilityHidden);
        }
    }
 
    //add custom menus
    $item = new ItemMenu(
        "wizard_end", ___("End creation", "ddui_tuto:wizard"),
        "#action/wizard.end"
    );
    $item->setBeforeContent('<div class="fa fa-check" />');
    $item->setHtmlAttribute("class", "menu--right");
    if (!empty($this->wizardInfos['nextStep'])) {
        $item->setVisibility($item::VisibilityHidden);
    }
    $item->useConfirm(___("Confirm end creation", "ddui_tuto:wizard"));
    $menu->appendElement($item);
 
    $item = new ItemMenu(
        "wizard_next", ___("Next step", "ddui_tuto:wizard"),
        "#action/wizard.next"
    );
    $item->setBeforeContent('<div class="fa fa-step-forward" />');
    $item->setHtmlAttribute("class", "menu--right");
    if (\DduiTuto\DDUI_TUTO_CONTACT__WFL::e_up_to_date === $document->getState()
        || !empty($this->wizardInfos['nextStep'])) {
        $item->setVisibility($item::VisibilityHidden);
    }
    $menu->appendElement($item);
 
    $item = new ItemMenu(
        "wizard_previous", ___("Previous step", "ddui_tuto:wizard"),
        "#action/wizard.previous"
    );
    $item->setBeforeContent('<div class="fa fa-step-backward" />');
    $item->setHtmlAttribute("class", "menu--right");
    if (empty($this->wizardInfos['previousStep'])) {
        $item->setVisibility($item::VisibilityDisabled);
    }
    $menu->appendElement($item);
 
    $item = new ItemMenu(
        "wizard_cancel", ___("Cancel", "ddui_tuto:wizard"),
        "#action/wizard.cancel"
    );
    $item->setBeforeContent('<div class="fa fa-undo" />');
    $item->setHtmlAttribute("class", "menu--left");
    $menu->appendElement($item);
 
    return $menu;
}

5.2.4.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-40

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.4.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que le menu présente de nouvelles entrées en lieu et place des anciennes.

5.2.5 Partie serveur : Personnalisation du corps du document

Le corps du document sera rendu au moyen d'un template personnalisé afin d'afficher les frames sans afficher les onglets. Ce template sera généré à la volée à partir des informations des vues.

5.2.5.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-40.

5.2.5.2 Code

La méthode getTemplates est mise à jour dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

public function getTemplates(\Doc $document = null)
{
    $this->initWizardInfos($document);
 
    $templates = parent::getTemplates($document);
 
    $templates["sections"]["menu"]["file"]
        = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/contactWizardHeader.mustache";
 
    $contentTemplate = "";
    foreach($this->wizardInfos['currentStep']['attributes']['frames'] as $frame) {
        $contentTemplate .= "{{{document.attributes." . $frame['attrid'] . ".htmlView}}}";
    }
 
    $templates["sections"]["content"]["content"] = $contentTemplate;
 
    return $templates;
}

5.2.5.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-50

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.5.4 Le résultat

Aucun changement n'est perceptible dans cette étape puisque la vue affichée ne contient pas d'onglet.

5.2.6 Partie serveur : Envoi des informations des vues

Les informations sur les vues seront rendues disponibles au client au moyen des customServerData

5.2.6.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-50.

5.2.6.2 Code

La méthode getCustomServerData est surchargée dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

public function getCustomServerData(\Doc $document)
{
    $this->initWizardInfos($document);
 
    $customServerData = parent::getCustomServerData($document);
 
    $customServerData["wizardInfos"] = $this->wizardInfos;
 
    return $customServerData;
}

5.2.6.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-60

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.6.4 Le résultat

Aucun changement n'est perceptible dans cette étape car ces données seront utilisées plus tard par le code javascript.

5.2.7 Partie client : rafraîchissement du bandeau de navigation

5.2.7.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-60.

5.2.7.2 Code

Le code javascript va être enregistré dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js :

(function wizardContact(window, $){
    "use strict";
 
    var updateSummaryStatus = function updateSummaryStatus() {
        $('.wizard_summary__step').each(function updateSummaryStatus_stepIteratee() {
            var $this = $(this),
                $unfilledStepAttributes = $('.wizard_summary__step__attribute', $this)
                .not('.wizard_summary__step__attribute_filled');
 
            $this.toggleClass(
                'wizard_summary__step_required_unfilled',
                0 < $unfilledStepAttributes.filter('.wizard_summary__step__attribute_required').length
            ).toggleClass(
                'wizard_summary__step_optional_unfilled',
                0 < $unfilledStepAttributes.not('.wizard_summary__step__attribute_required').length
            );
        });
    };
 
    window.dcp.document.documentController(
        "addEventListener",
        "ready",
        {
            "name": "addWizardEvents",
            "documentCheck": function addWizardEventsDocumentCheck(documentObject)
            {
                return documentObject.family.name === "DDUI_TUTO_CONTACT" &&
                    documentObject.renderMode === "edit";
            }
        },
        function addWizardEvents(/*event, documentObject*/)
        {
            updateSummaryStatus();
        }
    );
})(window, $);

Ce fichier javascript est ensuite injecté en surchargeant la méthode getJsReferences dans la classe ContactWizardRenderConfigEdit dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactWizardRenderConfigEdit.php :

public function getJsReferences(\Doc $document = null)
{
    $version = \ApplicationParameterManager::getParameterValue(
        "CORE", "WVERSION"
    );
 
    $jsReferences = parent::getJsReferences($document);
 
    $jsReferences['DDUI_TUTO_CONTACT_WIZARD']
        = "DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js?ws="
        . $version;
 
    return $jsReferences;
}

5.2.7.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-70

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.7.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que les chevrons sont colorés en fonction des attributs renseignés.

5.2.8 Partie client : Gestion des menus

Lors du click sur les éléments de menu, un événement de type action est déclenché.

5.2.8.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-70.

5.2.8.2 Code

L'événement déclenché au click sur un menu événement sera capturé par l'ajout d'un écouteur actionClick.wizard.contact dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js :

this.documentController(
    "addEventListener",
    "actionClick",
    {
        "name": "actionClick.wizard.contact",
        "documentCheck": function actionClickWizardContactDocumentCheck(documentObject)
        {
            return documentObject.family.name === "DDUI_TUTO_CONTACT" &&
                documentObject.renderMode === "edit";
        }
    },
    function actionClickWizardContact(event, documentObject, options)
    {
        var customServerData = this.documentController("getCustomServerData"),
            currentWizardStepName = null;
 
        if(customServerData && customServerData.wizardInfos && customServerData.wizardInfos.currentStep) {
            currentWizardStepName = customServerData.wizardInfos.currentStep.cv_idview;
        }
 
        switch (options.eventId) {
            case 'wizard.cancel':
                this.documentController(
                    "reinitDocument",
                    {
                        viewId: '!defaultConsultation'
                    }
                );
                break;
            case 'wizard.previous':
            case 'wizard.next':
                // save document and send customClientData with goto and currentStepName
                this.documentController(
                    "saveDocument",
                    {
                        customClientData: {
                            goto: options.eventId,
                            currentWizardStepName: currentWizardStepName
                        }
                    }
                ).then(function actionClickWizardContact_success(successInfos)
                {
                    successInfos.element.documentController(
                        "showMessage",
                        "Document has been saved"
                    );
                }, function actionClickWizardContact_error(errorInfos)
                {
                    errorInfos.element.documentController(
                        "showMessage",
                        {
                            type: 'error',
                            message: 'an error occured: ' + errorInfos.errorMessage.contentText
                        }
                    );
                });
                break;
            case 'wizard.end':
                //TODO
                break;
            default:
                return;
        }
        event.preventDefault();
    }
);

5.2.8.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-80

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.8.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons qu'il est possible de naviguer d'étape en étape au moyen du menu.

5.2.9 Partie client : Gestion du clic sur une étape

5.2.9.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-80.

5.2.9.2 Code

Nous utiliserons JQuery pour déclencher un événement lors du clic sur une étape Cet événement sera capturé par l'ajout d'un écouteur actionClick.wizard.contact dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js :

this.documentController(
    "addEventListener",
    "custom:wizardgotostep",
    {
        "name": "gotoStep.wizard.contact",
        "documentCheck": function gotoStepWizardContactDocumentCheck(documentObject)
        {
            return documentObject.family.name === "DDUI_TUTO_CONTACT" &&
                documentObject.renderMode === "edit";
        }
    },
    function gotoStepWizardContact(event, targetStep)
    {
        var customServerData = this.documentController("getCustomServerData"),
            currentWizardStepName = null;
 
        event.preventDefault();
 
        if (customServerData && customServerData.wizardInfos && customServerData.wizardInfos.currentStep) {
            currentWizardStepName = customServerData.wizardInfos.currentStep.cv_idview;
        }
 
        this.documentController(
            "reinitDocument",
            {
                customClientData: {
                    goto: 'wizard.targetStep',
                    currentWizardStepName: currentWizardStepName,
                    targetStep: targetStep
                }
            }
        );
    }
);
$('.wizard_summary').on('click', '.wizard_summary__step', function stepLabelClick()
{
    window.dcp.document.documentController(
        "triggerEvent",
        "custom:wizardgotostep",
        $(this).data('viewid')
    );
});

5.2.9.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-90

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.9.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons qu'il est possible de naviguer d'étape en étape en cliquant sur le libellé des étapes.

5.2.10 Partie client : rafraîchissement du résumé lors des changements de valeur

5.2.10.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-90.

5.2.10.2 Code

Un écouteur change.wizard.contact, déclenché à chaque changement de valeur, est ajouté au fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js :

this.documentController(
    "addEventListener",
    "change",
    {
        "name": "change.wizard.contact",
        "documentCheck": function changeWizardContactDocumentCheck(documentObject)
        {
            return documentObject.family.name === "DDUI_TUTO_CONTACT" &&
                documentObject.renderMode === "edit";
        }
    },
    function changeWizardContact(event, documentObject, attributeObject, values)
    {
        $('.wizard_summary__step__attribute[data-attrid=' + attributeObject.id + ']')
            .toggleClass(
                'wizard_summary__step__attribute_filled',
                values.current.value !== ''
            );
 
        updateSummaryStatus();
    }
);

5.2.10.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-100

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.10.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que le bandeau de navigation est bien rafraîchi lors de l'ajout ou de la suppression d'une valeur dans le document.

5.2.11 Partie client : fin du wizard et changement d'état

Lorsque l'utilisateur clique sur le bouton de fin de wizard, le document est enregistré et change d'état.

5.2.11.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-100.

5.2.11.2 Code

Le clic sur ce bouton émet un événement de type action. Cet événement est traité en ajoutant la gestion de l'action wizard.end dans l'écouteur actionClick.wizard.contact dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/Layout/wizard.js. Il suffit de sauver le document puis d'appeler la méthode changeStateDocument du contrôleur de document :

this.documentController(
    "addEventListener",
    "actionClick",
    {
        "name": "actionClick.wizard.contact",
        "documentCheck": function actionClickWizardContactDocumentCheck(documentObject)
        {
            return documentObject.family.name === "DDUI_TUTO_CONTACT" &&
                documentObject.renderMode === "edit";
        }
    },
    function actionClickWizardContact(event, documentObject, options)
    {
        var customServerData = this.documentController("getCustomServerData"),
            currentWizardStepName = null;
 
        if(customServerData && customServerData.wizardInfos && customServerData.wizardInfos.currentStep) {
            currentWizardStepName = customServerData.wizardInfos.currentStep.cv_idview;
        }
 
        switch (options.eventId) {
            case 'wizard.cancel':
                this.documentController(
                    "reinitDocument",
                    {
                        viewId: '!defaultConsultation'
                    }
                );
                break;
            case 'wizard.previous':
            case 'wizard.next':
                this.documentController(
                    "saveDocument",
                    {
                        customClientData: {
                            goto: options.eventId,
                            currentWizardStepName: currentWizardStepName
                        }
                    }
                );
                break;
            case 'wizard.end':
                this.documentController(
                    "saveDocument",
                    {
                        customClientData: {
                            currentWizardStepName: currentWizardStepName
                        }
                ).then(function wizardEnd_changeState() {
                    window.dcp.document.documentController(
                        "changeStateDocument",
                        {
                            "nextState": "ctc_e2",
                            "transition": "ctc_t_e1__e2"
                        },
                        {
                            viewId: '!defaultConsultation',
                            revision: -1
                        }
                    );
                    }
                });
                break;
            default:
                return;
        }
        event.preventDefault();
    }
);

5.2.11.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-110

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.11.4 Le résultat

En accédant au contact Thomas ANDERSON en modification à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultEdition.html, nous constatons que le bouton de fin de wizard est fonctionnel.

5.2.12 Partie serveur : Prise en compte du libellé d'étape en consultation

En consultation, le menu de modification doit afficher l'étape en cours.

5.2.12.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-40-110.

5.2.12.2 Code

Cela se fait en modifiant le libellé du menu de modification par une surcharge de la méthode getMenu dans le fichier DDUI_TUTO/Families/DDUI_TUTO_CONTACT/ContactRenderConfigView.php :

public function getMenu(\Doc $document)
{
    $menu = parent::getMenu($document);
 
    $modifLabel = ___("Modify", "UiMenu");
 
    $wizardTags = $document->getUTag('wizard');
    if (false === $wizardTags) {
        $wizardTags = [];
    } else {
        $wizardTags = json_decode($wizardTags->comment, true);
    }
    if (isset($wizardTags['currentWizardStepName'])) {
        $modifLabel .= " (" . $wizardTags['currentWizardStepLabel'] . ")";
    }
 
    $modifMenu = $menu->getElement("modify");
    if (!is_null($modifMenu)) {
        $modifMenu->setTextLabel($modifLabel);
    }
 
    return $menu;
}

5.2.12.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-40-120

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

5.2.12.4 Le résultat

En accédant au contact Thomas ANDERSON en consultation à l'adresse http://localhost:8080/api/v1/documents/CONTACT_THOMAS_ANDERSON/views/!defaultConsultation.html, nous constatons que le bouton de modification reflète bien la dernière étape consultée.

5.3 Conclusion

Vous avez vu dans cette partie que tous les comportements par défaut du document sont personnalisables, au niveau serveur par du code PHP, et au niveau client par du code Javascript.

Ce code peut intercepter l'ensemble des comportements prédéfinis pour les modifier, mais il est également possible d'ajouter des données supplémentaires et de les traiter pour non seulement modifier, mais aussi étendre le comportement par défaut.

Enfin, couplés aux fonctionnalités de CSS vues au chapitre précédent, ces surcharges permettent de changer profondément la façon dont les documents sont présentés à l'utilisateur.

Dans la partie suivante, vous apprendrez à intégrer ces documents enrichis à une interface applicative complexe.

Chapitre 6 Intégration d'un document Dynacase Document UIs dans une interface riche

Le but est ici de manipuler le document depuis une autre interface. Il est ainsi possible de présenter des documents dans une interface spécifique, ainsi que de les manipuler simplement, sans devoir injecter de code dans la famille manipulée.

6.1 Principe

Ce tutoriel est livré avec une interface minimaliste de gestion des contacts, accessible à l'adresse http://localhost:8080/?app=SIMPLE_LIST.

Cette application affiche en colonne de gauche tous les documents de la famille Contact.

Pour simplifier le déroulement de ce chapitre, l'application génère déjà des événements et en écoute d'autres, aussi l'objet de ce chapitre est-il uniquement de manipuler le document lui-même.

Application de gestion des contacts

Figure 13. Application de gestion des contacts

6.2 Mise en œuvre

6.2.1 Chargement d'un document

Lors du clic sur une fiche résumé, l'application déclenche un événement documentElementClicked sur la window. Nous allons écouter cet événement et afficher le document dans la zone .documentWrapper.

6.2.1.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-00.

6.2.1.2 Code

Dans le fichier SIMPLE_LIST/Layout/custom.js, ajoutons un écouteur de l'événement documentElementClicked, qui charge le document demandé.

Créons également la méthode addDocumentListeners, déclenchée lors de l'initialisation du widget, et chargée d'attacher les écouteurs d'événements au widget. En particulier, cette méthode écoute l'événement ready du widget pour signaler à l'application que ce document est affiché.

var loadDocument = function loadDocument(fetchOptions)
    {
        //check if the widget has already been initialised
        if (_.isUndefined($documentWrapper.document("instance"))) {
            //if not, initialize it
            $documentWrapper.document(fetchOptions)
                //and attach listeners to the newly created widget
                .on("documentloaded", addDocumentListeners);
        } else {
            //if yes, reuse it
            $documentWrapper.document("fetchDocument", fetchOptions);
        }
    },
    addDocumentListeners = function addDocumentListeners()
    {
        // propagate that this document is opened each time it is reloaded
        $documentWrapper.document(
            "addEventListener",
            "ready",
            {
                "name": "ready.simple_list"
            },
            function simpleList_propagateReady(event, documentObject)
            {
                // refresh the currently selected document
                $window.trigger('documentOpened', $documentWrapper.document("getProperties"));
            }
        );
    };
 
$window.on('documentElementClicked', function onDocumentElementClicked(event, options)
{
    var fetchOptions = {
        "initid": options.initid,
        "viewId": "!defaultConsultation"
    };
    loadDocument(fetchOptions)
});

6.2.1.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-10.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.1.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, un clic sur un document de la liste de gauche l'ouvre dans la zone de droite.

6.2.2 Écoute des modifications du document

Nous allons réagir à l'événement change du widget.

6.2.2.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-10.

6.2.2.2 Code

Enrichissons la méthode addDocumentListeners du fichier SIMPLE_LIST/Layout/custom.js en écoutant l'événement change du widget pour signaler à l'application que ce document est modifié :

// propagate that this document has changed
$documentWrapper.document(
    "addEventListener",
    "change",
    {
        "name": "change.simple_list"
    },
    function simpleList_propagateChange(event, documentObject)
    {
        $window.trigger('documentChanged', documentObject);
    }
);

Ici, nous nous contentons de déclencher l'événement en charge de la mise à jour de l'application. Le comportement de l'application étant en dehors du scope de ce tutoriel, son contenu n'est pas présenté. Il est néanmoins accessible dans le code source dans le fichier SIMPLE_LIST/Layout/main.js.

6.2.2.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-20.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.2.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, lors de la modification d'un document ouvert, le statut modifié (matérialisé par une astérisque jaune) est reporté dans la liste de gauche.

6.2.3 Écoute de la sauvegarde d'un document

Nous allons réagir à l'événement afterSave du widget.

6.2.3.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-20.

6.2.3.2 Code

Enrichissons la méthode addDocumentListeners du fichier SIMPLE_LIST/Layout/custom.js en écoutant l'événement afterSave du widget pour signaler à l'application que ce document a été sauvegardé :

// propagate that this document has been saved
$documentWrapper.document(
    "addEventListener",
    "afterSave",
    {
        "name": "afterSave.simple_list"
    },
    function simpleList_propagateAfterSave(event, documentObject)
    {
        $window.trigger('documentSaved', documentObject);
    }
);

Ici, nous nous contentons de déclencher l'événement en charge de la mise à jour de l'application. Le comportement de l'application étant en dehors du scope de ce tutoriel, son contenu n'est pas présenté. Il est néanmoins accessible dans le code source dans le fichier SIMPLE_LIST/Layout/main.js.

6.2.3.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-30.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.3.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, lors de la modification du nom ou du prénom d'un contact, le nouveau titre est reporté dans la liste de gauche après la sauvegarde du document.

6.2.4 Affichage de l'interface de création

Lors du clic sur le bouton Nouveau Contact, l'application déclenche un événement buttonCreateClicked. Écoutons cet événement et affichons l'interface de création dans la zone .documentWrapper.

6.2.4.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-30.

6.2.4.2 Code

Dans le fichier SIMPLE_LIST/Layout/custom.js, ajoutons un écouteur de l'événement buttonCreateClicked, qui charge l'interface de création de la famille demandée :

$window.on('buttonCreateClicked', function onButtonCreateClicked(event, options)
{
    var fetchOptions = {
        "initid": options.famid,
        "viewId": "!defaultCreation"
    };
    loadDocument(fetchOptions);
});

6.2.4.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-40.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.4.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, un clic sur le bouton Nouveau Contact ouvre l'interface de création.

6.2.5 Écoute de la création d'un document

Il n'existe pas d'événement spécifique à la création d'un document. Pour détecter une création, nous allons écouter l'événement afterSave du widget, et signaler une création à l'interface dans le cas où le document n'avait pas d'initid.

6.2.5.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-40.

6.2.5.2 Code

Modifions l'écouteur de l'événement afterSave dans la méthode addDocumentListeners du fichier SIMPLE_LIST/Layout/custom.js :

// propagate that this document has been saved
$documentWrapper.document(
    "addEventListener",
    "afterSave",
    {
        "name": "afterSave.simple_list"
    },
    function simpleList_propagateAfterSave(event, currentDocumentObject, previousDocumentObject)
    {
        if(previousDocumentObject.initid === 0) {
            // if previous document had no initid, it is a creation
            $window.trigger('documentCreated', currentDocumentObject);
        } else {
            // otherwise, it is a simple save
            $window.trigger('documentSaved', currentDocumentObject);
        }
    }
);

6.2.5.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-50.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.5.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, lors de la création d'un contact, le nouveau contact est ajouté à la liste de gauche.

6.2.6 Écoute de la suppression d'un document

Nous allons réagir à l'événement afterDelete du widget.

6.2.6.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-50.

6.2.6.2 Code

Enrichissons la méthode addDocumentListeners du fichier SIMPLE_LIST/Layout/custom.js en écoutant l'événement afterDelete du widget pour signaler à l'application que ce document a été supprimé:

// propagate that this document has been deleted
$documentWrapper.document(
    "addEventListener",
    "afterDelete",
    {
        "name": "afterDelete.simple_list"
    },
    function simpleList_propagateAfterDelete(event, documentObject)
    {
        $window.trigger('documentDeleted', documentObject);
    }
);

Ici, nous nous contentons de déclencher l'événement en charge de la mise à jour de l'application. Le comportement de l'application étant en dehors du scope de ce tutoriel, son contenu n'est pas présenté. Il est néanmoins accessible dans le code source dans le fichier SIMPLE_LIST/Layout/main.js.

6.2.6.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-60.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.6.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, lors de la suppression d'un contact, sa fiche est supprimée de la liste de gauche.

6.2.7 Écoute de la restauration d'un document

Nous allons réagir à l'événement afterRestore du widget.

6.2.7.1 Récupération des sources

Les sources avant cette étape correspondent au tag step-50-60.

6.2.7.2 Code

Enrichissons la méthode addDocumentListeners du fichier SIMPLE_LIST/Layout/custom.js en écoutant l'événement afterRestore du widget pour signaler à l'application que ce document a été restauré :

// propagate that this document has been restored
$documentWrapper.document(
    "addEventListener",
    "afterRestore",
    {
        "name": "afterRestore.simple_list"
    },
    function simpleList_propagateAfterRestore(event, documentObject)
    {
        $window.trigger('documentRestored', documentObject);
    }
)

Ici, nous nous contentons de déclencher l'événement en charge de la mise à jour de l'application. Le comportement de l'application étant en dehors du scope de ce tutoriel, son contenu n'est pas présenté. Il est néanmoins accessible dans le code source dans le fichier SIMPLE_LIST/Layout/main.js.

6.2.7.3 Déploiement

Les sources telles que déployées à cette étape correspondent au tag step-50-70.

Le déploiement se fait au moyen du developer toolkit (pour plus d'explications sur les outils de développement, se rendre sur leur documentation).

La commande est donc :

6.2.7.4 Le résultat

Depuis l'application à l'adresse http://localhost:8080/?app=SIMPLE_LIST, immédiatement après la suppression d'un contact et que sa fiche ait été supprimée de la liste de gauche, il est possible de le restaurer. Sa fiche est alors ajoutée à nouveau à la liste de gauche.

6.3 Conclusion

Dans cette partie, vous avez appris à utiliser le widget de document.

Ce composant permet de simplement intégrer un ou plusieurs documents dans une interface complexe, en ayant une abstraction du fonctionnement interne du document. Il est piloté par des méthodes et émet des événements auxquels votre interface peut réagir.

Liste des illustrations

Licence

Ce document est publié sous licence CC http://creativecommons.org/licenses/by-nc-sa/2.0/fr/

Vous êtes libres :

Selon les conditions suivantes :

Édition

Quick Start
© Anakeen, Anakeen Labs <labs@anakeen.com>
Module Document User Interface, version 1.0
Édition 1
Publié le 03/08/2018

Ce livre a été produit avec easybook 4.8, logiciel libre et open source développé par Javier Eguiluz à l'aide de différents composants Symfony.

Anakeen

Créé en 1998, Anakeen est un éditeur expert dans l'amélioration de la gestion des processus et de l'information avec pour objectif principal : valoriser les fonctions support en les libérant des tâches à faible valeur ajoutée. Le résultat opérationnel a toujours été recherché par toutes les entreprises et particulièrement aujourd'hui où le moindre détail peut faire la différence afin d'être ou de rester compétitif sur son marché. Pour chaque fonction support, être en situation de valoriser sa contribution au résultat global de l’entreprise est plus que jamais devenu une nécessité.

Impliqué depuis 1998 dans le logiciel libre, Anakeen est un acteur majeur de la gestion de l'information. Nos contributions pour l'utilisation des standards ouverts, la garantie de l'accès au code source et la grande diversité de nos partenaires vous assure pérennité et réversibilité.

L'ensemble du code PHP de Dynacase Platform est disponible sous licence Open Source, le modèle de données est documenté et public. Mais plus que ça, le code source est commenté dans l'objectif de faciliter sa compréhension pour la réutilisation ou la modification. Aussi toute la documentation concernant le produit est mise en ligne sur www.dynacase.org.

En choisissant un logiciel Open Source, vous faites le choix de la sécurité, car vous avez l'assurance de vérifier le fonctionnement du logiciel et la qualité du code.

Nos offres et services, nous permettent d'assurer le financement du développement produit mais aussi de contribuer chaque jour à l'adoption du business model Open Source.