Personal tools
Se connecter S'inscrire

Aller au contenu. | Aller à la navigation

Navigation

Un devis pour votre site internet ?
Plus d'informations sur les modules Solgema ?

Contactez la société Bellevue  

notre distributeur en France

Actions sur le document
  • Envoyer
  • Imprimer
  • Basculer en mode plein écran
  • Bookmark
Vous êtes ici :AccueilSupportCréation de modules Plone

Création de modules Plone

Mots-clés associés : ,,,
Informations générales sur la création des modules Plone

Voici quelques généralités sur l'architecture et la création des modules sur Plone.

Pour la création de produits:

ZopeSkel ( Zope Skeleton )

installation

ajouter dans le buildout:

parts =
   ...
   zopeskel

[zopeskel]
# installs paster and Zopeskel
recipe = zc.recipe.egg
eggs =
   PasteScript
   ZopeSkel

ligne de commande pour ajouter un produit (dans le /src):

../bin/paster create -t plone Mon.Produit

Pour un produit installable depuis les modules de Plone, "Register Profile" > True.

Il faut ensuite ajouter le nouveau module dans le buildout

eggs =
   ...
   Mon.Produit

zcml =
  ...
  Mon.Produit

develop =
   ...
   src/Mon.Produit

Architecture d'un module

Éléments constituants

  • dossiers structure du module
  • .py fichiers python
  • .pyc fichiers python compilés
  • .zcml (Zope Configuration Markup Language) fichiers de configuration Zope
  • .xml généralement dans /profiles, fichiers de configuration pour d'autres modules, importés lors de l'installation du module.
  • .pt (Page Template) une page utilisant le langage "html" et "tal"
  • .txt souvent des fichiers de documentation, ne sont en général pas indispensables au module
  • .cfg configuration du module pour le buildout
  • autres (images, fichiers javascript, textes, etc), sont en général des resources

Fichiers particuliers

  • __init__.py : demande à python de compiler les scripts qui se trouvent dans le dossier où est placé le __init__.py.
  • configure.zcml : fichier principal de configuration pour Zope

 

Le ZCML

C'est un fichier au formattage xml avec son propre espace de noms. Cet espace de noms doit être déclaré dans la balise <configure> au début du fichier.

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

Toutes les balises utilisées dans le fichier .zcml doivent être importées depuis la balise <configure>.

Les profiles

Les profiles sont une collection de fichiers xml qui définissent ou configure certains modules du site plone.
Ils sont déclarés dans les .zcml par l'instruction <genericsetup:registerProfile> et sont importés au moment de l'installation du module depuis l'interface de configuration de Plone.

On y trouve par exemple:

  • rolemap.xml définition des rôles (onglet "security")
  • types.xml définition des types d'objets
  • jsregistry.xml et cssregistry.xml définition des javascripts et des css
  • catalog.xml définition de l'index et des metadata du catalogue (portal_catalog)
  • propertiestool.xml définition des propriétés du site dans site_properties
  • browserlayer.xml enregistrement du layer pour le produit

Création d'une page pour Plone 4.2 (Zope3 style)

Une page se compose essentiellement d'une classe python et en général d'un template html (page template .pt). Cette page doit ensuite être déclarée dans un fichier .zcml par l'instruction suivante:

La déclaration dans un .zcml

<browser:page
    for="Products.Archetypes.interfaces.IBaseObject"
    name="edit"
    factory=".edit.Edit"
    template="edit.pt"
    permission="zope.View" />

les attributs:

  • for : pour quel interface notre page sera disponible ( for="*" pour tous les interfaces )
  • name : le nom de la page
  • factory : la classe python ( class Edit(): ) qui se trouve dans le fichier "edit.py"
  • permission : la permission que doit avoir l'utilisateur pour voir cette page
  • template : la page template ( .pt ) contenant le code html de notre page.
  • allowed_attributes : les attributs de notre page qui vont pouvoir être appelés depuis une autre page ( à condition d'avoir la permission définie dans "permission".


Au démarrage de Zope, toutes les pages et adapteurs ( et autres ) déclarés dans les .zcml sont inscrits dans un registre.

La déclaration browser:page est en fait un adapteur avec des particularités déjà prédéfinies:

<adapter
    for="Products.Archetypes.interfaces.IBaseObject
         zope.publisher.browser.interfaces.IDefaultBrowserLayer"
    provides="zope.publisher.browser.interfaces.IBrowserPage"
    name="edit"
    factory=".edit.Edit"
    permission="zope.View" />

Une page Zope déclarée par browser:page est en réalité un adapteur dont les particularités sont de

  • ( dans "for" ) adapter une classe qui implémente un interface particulier ( IBaseObject ) et une deuxième classe qui implémente IDefaultBrowserLayer ( le "request" )
  • ( dans "provides" ) nous fournir l'interface IBrowserPage

 

La classe et le template

La classe utilisée pour une page doit être basée sur la classe BrowserView.

from Products.Five.browser import BrowserView
from zope.component import getMultiAdapter

class Edit(BrowserView):
    """la classe de la page edit"""

    def isAnonymous(self):
        portal_state = getMultiAdapter((self.context, self.request),
                                            name=u'plone_portal_state')

        return portal_state.anonymous()


Dans cette classe Edit, le context et le request sont accessibles par self.context et self.request, initialisés dans la fonction __init__ de la classe BrowserView.

Le template lié à cette page par la déclaration dans le .zcml et un fichier .pt contenant du html et du tal.

Depuis le template, la classe "Edit" définie pour notre page est accessible depuis la variable "view" et on peut appeler toutes les méthodes et les attributs de la classe "Edit" depuis "view":

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
      lang="en"
      metal:use-macro="context/main_template/macros/master"
      i18n:domain="plone">
    <body>

        <metal:main fill-slot="main">
            Suis-je anonyme? <span tal:content="view/isAnonymous"/>
        </metal:main>
    </body>
</html>

 

Appel de la page

On peut appeler cette page (=adapteur)  depuis le site en ajoutant le nom de la page dans l'url ( /@@edit ).
On peut également appeler cette page (=adapteur) depuis une classe python en utilisant getMultiAdapter. Cette fonction parcoure le registre de Zope en recherchant un adapteur multiple ( un adapteur qui adapte plus de une classe ) suivant les interfaces implémentés par les classes à adapter, suivant le nom, suivant l'interface fourni.

from zope.component import getMultiAdapter
from zope.publisher.browser.interfaces import IBrowserPage, IDefaultBrowserLayer
from Products.Archetypes.interfaces import IBaseObject

myPage = getMultiAdapter((context, request), IBrowserPage, name='edit')

# On peut ensuite tester:

print IBaseObject.providedBy(context)
True
# Veut dire que la classe du context implémente l'interface IBaseObject

print IDefaultBrowserLayer.providedBy(request)
True
# Veut dire que la classe du request implémente l'interface IDefaultBrowserLayer

print IBrowserPage.providedBy(myPage)
True
# Veut dire que la classe "Edit" définie dans edit.py implémente l'interface IBrowserPage

 

Pour faire en sorte que la page ne soit accesible que lorsque notre produit est installé, il faut créer un "layer" propre à notre produit.

Il faut en premier lieu créer un interface qui sera utilisé comme marqeur pour le request:

from zope.interface import Interface

class IMonProduitLayer(Interface):
    """Cet interface est declare dans /profiles/default/browserlayer.xml"""

Il faut ensuite créer le fichier browserlayer.xml dans le profile de défaut qui sera importé lors de l'installation de notre produit:

<?xml version="1.0"?>

<layers>
    <layer name="mon.produit.layer" 
           interface="Mon.Produit.Mon.Produit.interfaces.IMonProduitLayer" />
</layers>

Nous pouvons ensuite déclarer nos pages spécifiquement pour ce nouveau layer en utilisant l'attribut "layer":

<browser:page
    for="Products.Archetypes.interfaces.IBaseObject"
    name="edit"
    factory=".edit.Edit"
    template="edit.pt"
    permission="zope.View"
    layer=".interfaces.IMonProduitLayer" />

De la même manière on pourra surclasser des pages existantes en les déclarant dans notre produit et en y ajoutant l'attribut "layer".

Création d'un formulaire de base

Pour créer un formulaire dans l'architecture zope3, nous utilisons les classes définies dans le module z3c.form.

Il faut en premier lieu créer un interface qui sera la déclaration des champs (et méthodes) de notre formulaire dans un fichier python:

import zope.interface
import zope.schema
from z3c.form import form, field, button

class IArticle(zope.interface.Interface):

    prix = zope.schema.Float(title=u'Prix HT')

    tva = zope.schema.Float(title=u'TVA', description=u"TVA en pourcent")

On crée ensuite une classe qui sera une page montrant notre formulaire. Cette classe est basée sur form.Form qui est un la classe de base pour un formulaire standard, sans sauvegarde de données.

class ArticleForm(form.Form):
    fields = field.Fields(IArticle)
    ignoreContext = True # n'utilise pas le context pour obtenir les donnees des champs.
    label = u"Pour ajouter un prix"

    prixTTC = ''

    @button.buttonAndHandler(u'Calcule')
    def handleSoumettre(self, action):
        data, errors = self.extractData() #recupere les donnees depuis le request
        if errors:
            self.status = self.formErrorsMessage
            return
        if data.get('prix') and data.get('tva'):
            self.prixTTC = data.get('prix')*(1+data.get('tva')/100)

Enfin il s'agit "d'emballer" ce formulaire pour qu'il réagisse comme une page Plone standard en utilisant wrap_form. Ceci nous permettra notamment d'utiliser l'attribut "template" dans la déclaration zcml.

from plone.z3cform.layout import wrap_form
ArticleFormView = wrap_form(ArticleForm)

Il faut maintenant déclarer le formulaire dans le .zcml ( l'attribut template est optionnel ):


<browser:page
    for="*"
    name="edit_article"
    class=".article.ArticleFormView"
    template="articleform.pt"
    permission="cmf.ModifyPortalContent"
    layer=".interfaces.IMonProduitLayer"
    />

Depuis la page template "articleform.pt" on accède au formulaire (ArticleForm) en utilisant view/form_instance et on obtient l'affichage du formulaire avec tal:replace="structure view/contents"

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
      lang="en"
      metal:use-macro="here/main_template/macros/master"
      i18n:domain="plone">
<body>

    <metal:main fill-slot="main">
        <metal:main-macro define-macro="main">
            <h1 class="documentFirstHeading" tal:content="view/label">Title</h1>
            <div id="content-core" tal:content="structure view/contents" />
            <p tal:condition="view/form_instance/prixTTC">Le prix TTC est : <span tal:replace="view/form_instance/prixTTC"/></p>
        </metal:main-macro>
    </metal:main>

</body>
</html>

Pour sauvegarder les données du formulaire dans les objets du site

Nous allons d'abord utiliser le formulaire form.EditForm de z3c.form dans lequel sont déjà définies les méthodes pour le traitement des données reçues dans le request ainsi que l'action et le bouton de sauvegarde des données. Nous allons également remplacer l'attribut prixTTC par une méthode qui nous retournera le prix TTC du contexte.

class ArticleForm(form.EditForm):

    fields = field.Fields(IArticle)
#    ignoreContext = True # n'utilise pas le context pour obtenir les donnees des champs.
    label = u"Pour ajouter un prix"

    def prixTTC(self):
        article = IArticle(self.context, None)
        if article:
            return article.getTTC()
        return None

Il faut également créer une classe utilisée comme adapteur et qui va sauvegarder nos données prix et tva dans l'objet:

from persistent import Persistent
from zope.component import adapts
from zope.annotation import factory

class ArticleAdapterBase(Persistent):
    implements(IArticle)
    adapts(IBaseObject)

    prix = 0.0 #initialisation
    tva = 0.0 #initialisation

    def getTTC(self):
        return self.prix*(1+self.tva/100)

ArticleAdapter = factory(ArticleAdapterBase)

Il faut maitnenant enregistrer l'adapteur dans le fichier .zcml:

  <adapter
      for="Products.Archetypes.interfaces.IBaseObject"
      provides=".page.IArticle"
      factory=".page.ArticleAdapter"
      />

Désormais les données soumises par ce formulaire seront enregsitrées dans l'objet. Pour accéder à ces données, il faut récupérer notre adapteur : IArticle(objet) qui nous retournera : ArticleAdapterBase content les données (prix, tva) et les méthodes (getTTC).