Les viewlets

"L'amour c'est un bouquet de viewlets" (adapté de "Violettes impériales" par Vincent Scotto)

Author:Gilles Lenfant
Contributors:Thomas Desvenain
Version:1.0.0

Copyright (C) 2010 Gilles Lenfant.

Chacun est autorisé à copier, distribuer et/ou modifier ce document suivant les termes de la licence Paternité-Pas d'Utilisation Commerciale-Partage des Conditions Initiales à l'Identique 2.0 France accessible à http://creativecommons.org/licenses/by-nc-sa/2.0/fr

Le code source présent dans ce document est soumis aux conditions de la "Zope Public License", Version 2.1 (ZPL).

THE SOURCE CODE IN THIS DOCUMENT AND THE DOCUMENT ITSELF IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.

La lecture de ceci nécessite la connaissance préalable du langage ZCML, ainsi que de l'outil GenericSetup.

Introduction

Les différentes pages d'un site Plone sont publiées selon le modèle de la main_template depuis la version 1 de Plone. C'est toujours le cas, mais...

Dans les versions antérieures de Plone, cette template faisait appel à de nombreuses macros pour déployer les différents blocs de code HTML nécessaire à l'affichage d'une page.

Même si ceci est toujours - partiellement - vrai, pour aider le support des composants prévus pour les versions antérieures de Plone, l'utilisation des macros est remplacée par celle des content providers.

L'inclusion du HTML produit par un content provider dans la main_template ressemble à ceci :

<div id="portal-top" i18n:domain="plone">
    <div tal:replace="structure provider:plone.portaltop" />
</div>

Vous remarquez la présence de l'expression TAL provider. Dans le monde Zope 3, un provider est un multi adapter qui adapte le context, la request et la vue. Ici le provider est un viewlet manager.

Les viewlets fournissent une portion de code HTML. Plone passe par des viewlet managers - une sorte d'aggrégateur de viewlets - qui permettent d'ordonnancer les viewlets avec un maximum de flexibilité.

Cette souplesse permise par les viewlet managers rend de moins en moins nécessaire la surcharge de la main_template lors de la réalisation de vos skins personnelles. Comme nous le verrons plus loin, vous pourrez également lors d'une personnalisation graphique "extrême", ajouter vos propres viewlet managers.

Gestion interactive des viewlets

Pour mieux illustrer les propos ci-avant, Plone fournit un outil visualisant l'articulation des viewlet managers et l'arrangement des viewlets dans ceux-ci. Ouvrez - étant authentifié comme administrateur - un navigateur sur la racine de vote site Plone et prolongez l'URL par /@@manage-viewlets.

_images/manageviewlets.png

Vue @@manage-viewlets : ordonnancement et masquage des viewlets

Immédiatement, sous chaque cadre, vous trouvez le nom du viewlet manager, dont le plone.portaltop de l'extrait précédent.

A l'intérieur de chaque viewlet manager, vous pouvez voir la liste des viewlets qu'il fournit. Chaque nom de viewlet est accompagné :

  • de son rang - commençant par 0 - dans le viewlet manager entre parenthèses,
  • de deux flèches permettant de monter ou descendre l'ordre d'apparition de la viewlet dans le viewlet manager,
  • du lien hide permettant de masquer la viewlet.

Avec cet outil, en remontant la viewlet plone.global_sections en tête en quelques clics, on obtient le résultat suivant :

_images/deplace-globalnav.png

La viewlet plone.global_sections remontée en tête

Vous aurez également remarqué l'absence des colonnes de portlets dans le gestionnaire de viewlets. Celles-ci sont gérées dans un gestionnaire dédié, objet du chapitre sur Les portlets.

L'enregistrement en ZCML

Les viewlet managers standard de Plone, ainsi que la plupart des viewlets standard sont enregistrés dans le configure.zcml du package plone.app.layout.viewlets dont voici un extrait :

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">

    <!-- Register viewlet managers - used in plone's main_template -->

    <browser:viewletManager
        name="plone.htmlhead"
        provides=".interfaces.IHtmlHead"
        permission="zope2.View"
    />

    <browser:viewletManager
        name="plone.htmlhead.links"
        provides=".interfaces.IHtmlHeadLinks"
        permission="zope2.View"
    />
    ...
    <!-- Define some viewlets -->
    ...

    <!-- Render the browser title -->

    <browser:viewlet
        name="plone.htmlhead.title"
        manager=".interfaces.IHtmlHead"
        class=".common.TitleViewlet"
        permission="zope2.View"
    />

    <!-- Render the workflow history -->

    <browser:viewlet
        name="plone.belowcontentbody.workflowhistory"
        manager=".interfaces.IBelowContentBody"
        class=".content.WorkflowHistoryViewlet"
        permission="zope2.View"
    />
    ...

</configure>

Vous remarquez les deux éléments viewletManager et viewlet qui enregistrent - respectivement :

  • les viewlet managers utilisables dans une template comme la main_template,
  • les viewlets pouvant être associées aux différents types de viewlet manager.

Les attributs d'un élément "viewletManager" :

Attributs d'un élément viewletManager (* = obligatoire)
Attribut Description
name * Définition du nom du viewlet manager. Doit être unique dans l'instance Zope.
permission * Permission nécessaire pour voir le viewlet manager. Généralement zope2.View.
for Interface des contenus pour lesquels ce viewlet manager peut être utilisé. Défaut : zope.interface.Interface Exemple : Products.ATContentTypes.interface.IATDocument. Vous pouvez utiliser * pour signaler que le viewlet manager peut être utlisé dans n'importe quel contexte, ce qui est le cas la plupart du temps.
layer Interface de skin spécifique pour laquelle le viewlet manager peut être utilisé. Ce paramètre n'a d'intérêt que lors de la réalisation d'une skin, comme dans le cas du composant NuPlone. Par défaut : zope.publisher.interfaces.browser.IDefaultBrowserLayer.
view Interface de view pour laquelle le viewlet manager peut être utilisé. Défaut : zope.publisher.interfaces.browser.IBrowserView. Exemple : plone.app.content.browser.interfaces.IFolderContentsView. La valeur par défaut convient dans l'immense majorité des cas.
provides interface marqueur spécifique à ce viewlet manager. Bien que cet attribut soit facultatif, il est nécessaire, comme nous le verrons plus loin, de l'associer à une interface marqueur spécifique afin de pouvoir y associer des viewlets. Par défaut : zope.viewlets.interfaces.IViewletManager.
class * Éventuelle classe Python spécifique de ce viewlet manager. La classe par défaut est suffisante dans la majorité des cas. Si vous voulez donner à un administrateur la possibilité d'ordonner les viewlets - avec le panneau de contrôle vu plus haut - inscrites dans ce viewlet manager, la classe devra être : plone.app.viewletmanager.manager.OrderedViewletManager. Par défaut : zope.viewler.manager.ViewletManagerBase.
template * Éventuelle template devant être utilisée pour ce viewlet manager. Ceci peut être utilisé dans certains cas en alternative à l'attribut class vu ci-avant. Pour illustrer ceci, je vous invite à lire le code de Products.ResourceRegistries. À noter que cette façon d'opérer est rarement utilisée. Par défaut : None. Attention, vous devez fournir l'attribut class ou l'attribut template mais en aucun cas les deux.
allowed_interface Interface exposant les méthodes publiques de ce viewlet manager. Généralement inutile. Par défaut : None
allowed_attributes Liste d'attributs et méthodes publics (séparés par des espaces) de ce viewlet manager. Par défaut : None

Warning

Dans la définition ZCML d'un viewlet manager comme décrite ci-dessus, vous devez fournir l'attribut class ou l'attribut template mais en aucun cas les deux. De même, il est inutile de fournir allowed_interface et allowed_attributes.

Attributs d'un élément viewlet (* = obligatoire)
Attribut Description
name * Définition du nom de la viewlet. Doit être unique dans l'instance Zope.
permission * Permission de vue de cette viewlet. Généralement zope2.View
for Interface de types de contenus pour lesquels cette viewlet sera affichée. Par exemple, pour n'afficher cette viewlet que dans les vues d'un ATDocument (ou type dérivé), vous placerez : Products.ATContentTypes.interface.IATDocument. Par défaut : zope.interface.Interface. Pour que la viewlet s'affiche dans tout contexte, vous placerez *.
layer L'utilisation de cet attribut est identique à celle prévue pour le viewlet manager.
view L'utilisation de cet attribut est identique à celle prévue pour le viewlet manager.
manager L'interface de viewlet manager dans laquelle cette viewlet peut être inscrite. En d'autres termes, la valeur de cet attribut doit être identique à l'attribut "provides" de la déclaration ZCML du viewlet manager dans lequel vous voulez inscrire la présente viewlet. Par défaut : zope.viewlet.interfaces.IViewletManager, c'est-à-dire que par défaut, vous pourrez placer cette viewlet dans n'importe quel viewlet manager.
class La classe qui réalise cette viewlet. Cette classe doit avoir une méthode render qui produit l'extrait de HTML. Par défaut : None. Exemple : browser.maviewlet.MaViewlet
attribute La méthode de rendu de votre classe de viewlet, si celle-ci n'est pas render. Ceci n'a d'intérêt que pour avoir plusieurs méthodes de rendu pour une seule classe de viewlet.
template Vous pouvez fournir une template pour fournir l'extrait HTML lorsque vous ne désirez pas, ou ne pouvez pas, faire réaliser ceci par la méthode render de la classe mentionnée plus haut. Dans ce cas, l'objet instancié depuis cette dite classe est accessible dans votre template personnelle à travers l'objet "view". Cette approche est utilisée pour personnaliser des viewlets standard ou d'un composant tiers dans vos propres réalisations, sans avoir à faire une ligne de Python.
allowed_interface L'utilisation de cet attribut est identique à celle prévue pour le viewlet manager
allowed_attributes L'utilisation de cet attribut est identique à celle prévue pour le viewlet manager
foo bar. Non, ce n'est pas un gag, vous pouvez fournir des attributs unicode supplémentaires de cette façon à la viewlet. Ceux-ci peuvent être exploités dans le code Python de la viewlet sous forme d'attributs simple self.foo, ou dans la template dans l'expression TALES view/foo.

Si vous avez bien lu ce qui précède, vous savez comment invoquer un viewlet manager depuis une template quelconque, et déclarer la compatibilité d'une viewlet avec un viewlet manager, en faisant la liaison à l'aide de l'attribut "manager" d'une viewlet.

Le step GenericSetup

Malheureusement ceci peut ne pas suffire ! Les déclarations zcml permettent d'associer les viewlets à des viewlet managers, mais pas de définir l'ordre des viewlets au sein du manager... Ceci est réalisé par un step "GenericSetup" dédié, le bien nommé viewlets.xml.

Ordre des viewlets

La mise en place d'un profil complet GenericSetup fait l'objet d'un chapitre de la documentation intégrateur ; nous ne nous attarderons que sur ce step spécifique :

<?xml version="1.0"?>
<object>
    <order manager="monproduit.monmanager" skinname="*">
        <viewlet name="monproduit.maviewlet" />
        <viewlet name="monproduit.monautreviewlet" />
    </order>
</object>

L'élément <order ...> déclare le viewlet manager dans lequel la viewlet sera insérée. Notez que ceci ne crée pas le viewlet manager, la création de celui-ci étant faite dans le fichier ZCML vu dans le paragraphe précédent.

La valeur de l'attribut manager doit correspondre à celle de l'attribut "name" du viewlet manager vu dans sa déclaration ZCML.

De même, la valeur de l'attribut name des éléments <viewlet ...> doivent correspondre à l'attribut name des éléments <viewlet ...> du fichier configure.zcml.

La valeur de l'attribut skinname permet de ne fournir le paramétrage des viewlets que pour une skin, en donnant le nom de cette skin. Par exemple Plone Default, ou My Beautiful Skin. Dans l'exemple ci-avant, la valeur * insère la viewlet pour toutes les skins.

La déclaration minimale de step telle que fournie ci-avant remplace l'ensemble des viewlets éventuellement insérées par d'autres steps viewlets.xml dans le viewlet manager monproduit.monmanager. Ceci est sans doute l'effet voulu dans votre cas.

Dans de nombreux autres cas, il est également possible d'ajouter des viewlets dans des viewlet managers existants fournis par Plone ou des produits tiers.

Dans ce cas, on utilisera plutôt la notation suivante :

<?xml version="1.0"?>
<object>
    <order manager="plone.portaltop" skinname="*">
        <viewlet name="monproduit.maviewlet" insert-after="plone.header"/>
    </order>
</object>

Facile à comprendre : on ajoute pour toutes les skins, dans le viewlet manager plone.portaltop, la viewlet monproduit.maviewlet après la viewlet de nom plone.header.

L'attribut insert-after peut également prendre la valeur * pour signifier que la viewlet est insérée en dernière position.

Il est également possible, dans le même ordre d'idées, d'insérer l'attribut insert-before. Je n'insulterai pas votre intelligence en décrivant les valeurs possibles de cet attribut.

Viewlets cachées

De même, si vous désirez masquer certaines viewlets déclarées dans du code sur lequel vous n'avez pas la main, vous devez passer par le step : viewlets.xml.

Il vous suffira d'ajouter un élément <hidden ...>, dont la syntaxe est similaire à celle de <order ...>. Ici, nous masquons le path_bar, appelé aussi breadcrumbs ou navigation horizontale :

<hidden manager="plone.portaltop" skinname="Formation Theme" purge="true">
  <viewlet name="plone.path_bar" />
</hidden>

Exemple

Nous allons faire très simple, ici. Vous trouverez un exemple plus complet de création de viewlet dans le chapitre Création d'un thème Plone.

Imaginons que vous vouliez que la date d'expiration d'un document apparaisse pour les modérateurs du site, sous la ligne d'information de modification (Par xxx - Dernière modification le...) : c'est un cas spécifique qui n'est pas fourni par défaut dans Plone, mais qui est peut-être indispensable à l'application de vos méthodes de travail.

Nul besoin de modifier la template principale ou quoi que ce soit d'autre. Vous pouvez ajouter un élément en ajoutant une viewlet sans intervenir sur le reste du code.

Ajoutez un module viewlets.py dans formation.theme, dans lequel vous ajouterez une classe ExpirationDateViewlet qui hérite de ViewletBase :

class ExpirationDateViewlet(ViewletBase):

    def render(self):
        expires = self.context.getExpirationDate()
        return expires and '<div class="documentByLine">Expire le : %s</div>' % expires.Date() or None

Éditez ainsi le fichier configure.zcml de votre produit formation.policy pour déclarer votre viewlet (vous devrez peut être ajouter le namespace xmlns:browser="http://namespaces.zope.org/browser").

<browser:viewlet
    name="formation.expirationdate"
    for="Products.ATContentTypes.interface.IATContentType"
    manager="plone.app.layout.viewlets.interfaces.IBelowContentTitle"
    class=".viewlets.ExpirationDateViewlet"
    permission="cmf.ReviewPortalContent"
    />

Vous n'auriez probablement pas su quoi mettre dans manager. Pour le savoir, il faut consulter la vue @@manage-viewlets, où vous pourrez trouver l'interface du ViewletManager que vous voulez compléter.

Redémarrez votre site, vous observerez le nouvel élément sur votre page d'accueil (si vous lui avez donné une date d'expiration...).

_images/expirationdate.png

Viewlet de la date d'expiration

Pour aller plus loin

Comme d'habitude, pour comprendre les détails de fonctionnement des mécanismes définis dans ce chapitre, il est nécessaire de lire le code source des modules suivants :

Code à lire également
Module Description
plone.app.layout Les ressources responsables de la mise en page d'un site Plone
plone.app.layout.viewlets Les ressources fournissant les viewlet managers et viewlets standard de Plone.
zope.publisher.browser Les ressources Zope 3 de base des viewlets.

Table des matières

Sujet précédent

Création d'un thème Plone

Sujet suivant

Les portlets

Cette page


blog comments powered by Disqus