Orchard – Création d’un widget – Partie 2

250px-Orchard_logo_1.svg

Dans cette seconde partie du tutorial (voir première partie ici) sur la création d'un widget dans Orchard, nous nous concentrerons sur la mise en place des derniers éléments nécessaires à l'affichage côté client du widget et son utilisation dans le back-end du CMS.

1 - Handler

Il est nécessaire d’ajouter un handler au module, un objet comparable aux filtres dans une application classique ASP.NET MVC. Dans notre situation, celui-ci permettra d'utiliser le repository se chargeant de gérer les instances de notre widget. Pour plus d'information sur les handlers dans Orchard, je vous invite à consulter cette page de la documentation officielle.

using MyWidget.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
Chemin: ~/Handlers/MyGridWidgetParthandler.cs

2 – Driver

L’affichage d’un widget nécessite un driver, un type d’objet spécifique à Orchard similaire au controller dans une application ASP.NET MVC classique. Sa fonction principale est de répondre aux différentes demandes d'affichage ou d'édition de notre widget au sein du layout actif / backend.

Pour fonctionner, notre driver a besoin de trois fonctions: une méthode Display et deux méthodes Editor.

using MyWidget.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;

namespace MyWidget.Drivers
{
    public class MyGridContentPartDriver : ContentPartDriver<MyGridWidgetPart>
    {
        protected override string Prefix
        {
            get { return "MyGridWidgetPart"; }
        }

        protected override DriverResult Display(MyGridWidgetPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_MyGridContentPartView",
                () =>
                {
                    return shapeHelper.Parts_MyGridContentPartView(
                        Id: part.Id,
                        ButtonCountProperty: part.ButtonCount,
                        HeightProperty: part.Height,
                        InputProperty: part.Input,
                        PreviousNextProperty: part.PreviousNext,
                        NumericProperty: part.Numeric,
                        PageSizeProperty: part.PageSize,
                        ControllerUrlProperty : part.ControllerUrl);
                });
        }

        protected override DriverResult Editor(MyGridWidgetPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_MyGridContentPartEditorView_Edit",
             () => shapeHelper.EditorTemplate(
                 TemplateName: "Parts/MyGridContentPartEditorView",
                 Model: part,
                 Prefix: Prefix));
        }

        protected override DriverResult Editor(MyGridWidgetPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }
    }
}
Chemin : ~/Drivers/MyGridWidgetContentPartDriver.cs

La méthode Display génère une shape (une vue spéciale à Orchard) utilisée lors du rendu du widget avec les différentes propriétés du widget. Les méthodes Editor (GET/POST) permettent d’afficher la vue d’édition et d’enregistrer les modifications apportées aux propriétés du widget. C'est ici qu'il est possible de mettre en place une validation serveur des données saisies par l'utilisateur.

3 – Controller

Lors de la création du modèle de notre widget en partie 2, nous avons défini une propriété « ControllerUrl ». Celle-ci a pour but de permettre à l’utilisateur d’avoir le choix entre plusieurs sources de données.

Le driver étant dédié aux mécaniques internes d'Orchard, nous allons créer un simple controller contenant une méthode renvoyant un objet JSON prenant en paramètre d'appel le type de données à afficher.

using System.Collections.Generic;
using System.Web.Mvc;
using MyWidget.Models.VM;

namespace MyWidget.Controllers
{
    public class MyWidgetController : Controller
    {
        public ActionResult GetData(string type)
        {
            if (type == "ALARME")
            {
                var retour = new List<AlarmeViewModel>
                {
                    new AlarmeViewModel{ Matricule = "ALM-01", Name = "Alarme 01", Type = "Electronique"},
                    new AlarmeViewModel{ Matricule = "ALM-02", Name = "Alarme 02", Type = "Mécanique"},
                    new AlarmeViewModel{ Matricule = "ALM-03", Name = "Alarme 03", Type = "Electronique"},
                    new AlarmeViewModel{ Matricule = "ALM-04", Name = "Alarme 04", Type = "Virtuelle"},
                    new AlarmeViewModel{ Matricule = "ALM-03", Name = "Alarme 03", Type = "Electronique"},
                    new AlarmeViewModel{ Matricule = "ALM-06", Name = "Alarme 06", Type = "Virtuelle"},
                };

                return Json(retour, JsonRequestBehavior.AllowGet);
            }

            if (type == "EQUIPEMENT")
            {
                var retour = new List<EquipementViewModel>
                {
                    new EquipementViewModel{ Matricule = "EQUIP-01", Name = "Equipement 01", Type = "Electronique"},
                    new EquipementViewModel{ Matricule = "EQUIP-02", Name = "Equipement 02", Type = "Mécanique"},
                    new EquipementViewModel{ Matricule = "EQUIP-03", Name = "Equipement 03", Type = "Electronique"},
                    new EquipementViewModel{ Matricule = "EQUIP-04", Name = "Equipement 04", Type = "Electronique"},
                    new EquipementViewModel{ Matricule = "EQUIP-03", Name = "Equipement 03", Type = "Mécanique"},
                    new EquipementViewModel{ Matricule = "EQUIP-06", Name = "Equipement 06", Type = "Mécanique"},
                };

                return Json(retour, JsonRequestBehavior.AllowGet);
            }

            return Json("{}", JsonRequestBehavior.AllowGet);
        }
    }
}
Chemin: ~/Controllers/MyGridController.cs

Il est aussi nécessaire de créer les modèles de données liées dont voici l’implémentation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyWidget.Models.VM
{
    public class AlarmeViewModel
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string Matricule { get; set; }
    }

    public class EquipementViewModel 
    { 
       public string Name { get; set; } 
       public string Type { get; set; } 
       public string Matricule { get; set; } }
    }
}
Chemin : ~/Models/VM/WidgetViewModels.cs

 

4 – Vues et bundles

 1 – Bundles

A l'instar d'une application ASP MVC, Orchard permet d’utiliser des bundles de ressources. Cependant ceux-ci s'avèrent peu pratique à utiliser, c'est pourquoi il est conseillé d'utiliser l'extension Web Essentials pour Visual Studio qui permet de générer un bundle de nos ressources. Pour l'instant notre bundle ne contient que les différentes ressources de Kendo, nous ajouterons par la suite d'autres fichiers mettant en place la grid Kendo du widget.

Pour utiliser nos ressources au sein du widget, nous créons une classe ResourceManifest implémentant l'interface IResourceManifestProvider fournie par Orchard.

using Orchard.UI.Resources;

namespace MyWidget
{
    public class ResourceManifest : IResourceManifestProvider
    {
        public const string WidgetBundle = "WidgetBundle";
     
        public void BuildManifests(ResourceManifestBuilder builder)
        {
            var manifest = builder.Add();

            manifest.DefineScript(WidgetBundle)
                .SetUrl("MyWidgetBundle.js");

            manifest.DefineStyle(WidgetBundle)
               .SetUrl("MyWidgetBundle.css");
        }
    }
}
Chemin: ~/ResourceManifest.cs

L'utilisation des fonctions DefineScript et DefineStyle de l'objet ResourceManifestBuilder dans la méthode BuildManifests permettent à Orchard de trouver les fichiers adéquats respectivement dans les dossier Scripts et Styles du module lors de son utilisation.

 2 - Vues

Comme vu lors de l'étape précédente, nous allons avoir besoin de deux vues : Une vue pour la création/édition du widget dans le backend et une autre vue pour l'affichage dans le site.

Notre première vue contient les différents contrôles offerts à l’utilisateur pour personnaliser son instance de widget dans l’administration du site. Pour cela, nous créons un nouveau fichier MyGridContentPartEditorView.cshtml pour lequel nous spécifions un modèle de type MyGridWidgetPart, envoyé par la méthode Editor du driver.

La propriété ControllerUrl utilise deux contrôles RadioButton afin de spécifier le paramètre qui est envoyé à la méthode GetData() de notre controller.

@model MyWidget.Models.MyGridWidgetPart

<fieldset>
    <legend>@T("Grid's properties")</legend>

    <div class="editor-label">
        Activer la pagination numérique
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.Numeric)
    </div>
    <br />

    <div class="editor-label">
        Nombre de boutons à afficher
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.ButtonCount)
    </div>
    <br />

    <div class="editor-label">
        Afficher un champs de recherche par page
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.Input)
    </div>
    <br />

    <div class="editor-label">
        Afficher les controles précédent/suivant
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.PreviousNext)
    </div>
    <br />

    <div class="editor-label">
        Nombre d'éléments par page
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.PageSize)
    </div>
    <br />

    <div class="editor-label">
        Hauteur du tableau
    </div>
    <div class="editor-field">
        @Html.EditorFor(x => x.Height)
    </div>
    <br />

    <div class="editor-label">
        Source de données
    </div>
    <div class="editor-field">
        @Html.RadioButtonFor(x => x.ControllerUrl, "EQUIPEMENT") Liste Equipements
        @Html.RadioButtonFor(x => x.ControllerUrl, "ALARME") Liste Alarmes
    </div>

</fieldset>
Chemin : ~/Views/EditorTemplates/Parts/ MyGridContentPartEditorView.cshtml

La seconde vue se charge du rendu côté front-end. La div principale possède des attributs HTML qui représente les différentes propriétés du widget définies par l’utilisateur dans le dashboard. Nous n’utilisons pas d’attribut ID pour notre div car il est possible d’avoir plusieurs instances d’un même widget au sein d’une page Orchard. L’utilisation d’une classe permet de s’assurer que chacun de ceux-ci posséderont leurs propres propriétés.

@using MyWidget
@{
    Style.Require(ResourceManifest.WidgetBundle).AtHead();
    Script.Require(ResourceManifest.WidgetBundle).AtHead();
    Script.Require("jQuery").AtHead();
}

<div class="grid" 
data-input="@Model.InputProperty" 
data-numeric="@Model.NumericProperty"
data-previousnext="@Model.PreviousNextProperty" 
data-buttoncount="@Model.ButtonCountProperty"
data-pagesize="@Model.PageSizeProperty" 
data-height="@Model.HeightProperty"
data-controller-url="@Url.Action("GetData", "MyWidget", new { type = Model.ControllerUrlProperty, area = "MyWidget" })">
</div>
Chemin: ~/Views/Parts/ MyGridContentPartView.cshtml

Notez l’ajout des bundles précédemment crée dans notre vue. Cela permet à Orchard de n’inclure ces ressources que lorsqu’une page du site contient une instance de notre widget. Dans le cas où notre page contient plusieurs instances de notre widget, alors Orchard se chargera de ne les inclure qu’une seule fois.

Maintenant que nos vues sont prêtes, il ne nous reste plus qu’à créer les modules javascripts qui initialisent la grille kendo sur la div ayant pour classe « Grid ».

 3 -  Côté javascript

Fichier widgetModule.js

Ce fichier javascript contient un plugin jquery avec une méthode « setup » publique. On ne nomme pas expressément l’élément HTML sur lequel nous souhaitons créer la grille Kendo, laissant cette tâche à notre widgetLoader.

Il est important de noter que le code de notre fonction passe par une boucle foreach. Comme dit précédemment, au sein d’Orchard, chacun des widgets pouvant apparaître plusieurs fois sur une même page, il est nécessaire d’utiliser une classe (grid dans notre cas, cf ci-dessus) et donc d’exécuter la méthode pour chacun des éléments l'implémentant afin de s’assurer que les propriétés d’un widget n’influent pas sur celles d'un autre.

Etant donné que nous avons défini sur l’unique div de notre widget un certain nombre de data-attributes, il est simple de les récupérer par la suite dans notre méthode.

(function ($, window, undefined) {

    var publicMethods = {

        setup: function () {

            this.each(function () {

                var controllerUrlVal = $(this).data("controller-url");
                var buttoncountVal = $(this).data("buttoncount");
                var inputVal = $(this).data("input") === "True" ? true : false;
                var heightVal = $(this).data("height") === "0" ? "auto" : $(this).data("height");
                var numericVal = $(this).data("numeric") === "True" ? true : false;
                var pagesizeVal = $(this).data("pagesize");
                var previousnextVal = $(this).data("previousnext") === "True" ? true : false;

                $(this).kendoGrid({
                    dataSource: {
                        transport: {
                            read: controllerUrlVal
                        },
                        pageSize: pagesizeVal,
                    },
                    height: heightVal,
                    scrollable: true,
                    sortable: true,
                    filterable: true,
                    pageable: {
                        input: inputVal,
                        numeric: numericVal,
                        previousNext: previousnextVal,
                        buttonCount: buttoncountVal,
                    },
                    columns: [
                        { field: "Name", title: "Name", width: "130px" },
                        { field: "Matricule", title: "Matricule", width: "130px" },
                        { field: "Type", title: "Type", width: "130px" }
                    ]
                });
            });
        },
    };

    $.fn.MyGridWidget = function (method) {
        return publicMethods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    };

})(jQuery, window);
Chemin: ~/Scripts/widgetModule.js
Fichier widgetLoader.js

Le second fichier javascript se contente d’invoquer la méthode setup de notre plugin jquery sur chacun des éléments HTML ayant la classe « grid » lorsque la page a fini de charger.

(function ($, window, undefined) {
    $(document).ready(function () {
        $(".grid").MyGridWidget("setup");
    });
})(jQuery, window);
Chemin: ~/Scripts/widgetLoader.js

5 – Ajout du widget au site

Maintenant que notre widget est prêt, il ne reste plus qu’à configurer Orchard pour l’activer et le déployer au sein d’une page.

1 – Activer la migration

Une fois connecté dans l’interface administrateur grâce au login défini pendant l’installation, il est nécessaire de commencer par activer le module « Migrations » dans la section « Modules », ce qui permettra lors de l’activation de notre module d’exécuter la migration définie en étape 4 de la première partie du tutorial.

wb_widget_migration_activation

2 – Activer le module MyWidget

Nous pouvons maintenant activer notre module qui se trouvera dans la section « Uncategorized ». L’entrée représentant notre widget indique que notre module est dépendant des modules jQuery et Widgets comme nous l’avions défini dans le manifeste du module en étape 2.

Si tout s’est bien déroulé, le visuel après activation devrait être le suivant :

wb_activation_module

3 – Ajouter une instance du widget

Pour finir, il ne nous reste plus qu’à ajouter une instance de notre widget dans une page d’Orchard (la home dans notre cas).

Pour cela, on se dirige vers la section « Widget » du dashboard, on sélectionne une zone dans laquelle le widget sera placé et on clique sur le bouton d’ajout. Dans la page suivante, on sélectionne le widget "My Grid Widget", ce qui nous amène sur la page de configuration.

Les différents contrôles de notre vue d’édition (définis pendant l’étape 8) se trouvent en bas de page comme montré sur le lien suivant. Les autres contrôles sont eux ajoutés automatiquement par Orchard pour tous les widgets.

Le rendu côté client après la création des widgets est visible dans l'image ci-dessous. Comme on peut le voir, chacun des widgets a des propriétés différentes sans nécessiter d'intervention dans le code.

wb_module_activated

6 – Conclusion

La création d’un module et plus particulièrement d’un widget peut paraître compliquée au premier abord face à la quantité d’étapes à mettre en place, cependant une fois la prise en main des concepts clefs du CRM effectuée, la création devient sensiblement plus fluide et se trouve être très similaire à celle d'une application ASP MVC.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.