\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage[frenchb]{babel}

\newcommand{\zope}{\emph{Zope }}
\newcommand{\gadfly}{\emph{Gadfly }}

\title{Tutorial Zope : utilisation de base (Zope 1.x)}
\author{Benoît \textsc{Rouits} {\texttt{<brouits@free.fr>}}}
\date{2001} % N'affiche pas la date
\begin{document}
\maketitle
\tableofcontents
\newpage
\zope signifie ``\zope Object Publishing Environment''.  C'est un serveur
web applicatifs en open source orienté objet. Le site officiel de \zope est
à l'adresse \verb"http://www.zope.org/".
\zope est lui-même une application web: gestion et développement se font via 
un navigateur. Au lieu de publier des fichiers HTML pointés par une URL,
\zope accède à des objets qui savent comment s'afficher (généralement en HTML).
Les URLs de \zope sont souvent des appels de fonctions. Par exemple,
\verb"http://www.truc.com/account/main" peut être traduit par \zope à un 
appel de la méthode \texttt{main()} de l'objet \texttt{account}.
\section{Créer des documents composites DTML}
DTML (Document Template Markup Language) est le langage de présentation de données de \zope pour le web. U document DTML peut appeler des objets \zope
comme des méthodes DTML, des scripts Python ou Perl, des méthodes SQL, des images, etc\dots
Sur Zope 1.x et 2.x, pour créer un document DTML, on clique sur \texttt{[Select type to add]} et on sélectionne 
\texttt{DTML document} puis on clique sur \texttt{add}. On donnera comme nom à ce 
document \texttt{index\_html}. Voici un exemple de document DTML:
\begin{verbatim}
<dtml-var standard_html_header>
<H1>Hello World !</H1>
<dtml-var standard_html_footer>
\end{verbatim}
On peut constater que ce document appelle deux objets : \texttt{standard\_html\_header} et
\texttt{standard\_html\_footer} qui sont des objets \zope. Ces objets n'on pas besoin d'être
dans le même \emph{Folder} que notre document. En effet, \zope va les rechercher dans son
espace de nommage qui englobe le \emph{Folder} courant et ses \emph{Folders} parents. On
appelle cela le principe d'acquisition. On peut par exemple surcharger
\texttt{standard\_html\_header} en le fabricant dans le répertoire courant. 
Voici un exemple d'un nouveau \texttt{standard\_html\_header}:
\begin{verbatim}
<HTML>
<BODY>
\end{verbatim}
Celui-ci sera pris en compte car il est le premier rencontré dans la pile des noms de \zope
depuis \texttt{index\_html}. 
\subsection{Présentation conditionnelle : balise \emph{if}}
DTML peut faire de la présentation conditionnelle:
\begin{verbatim}
<dtml-if expr="num > 5">
<dtml-var num> est supérieur à 5.
<dtml-elif expr"num < 5">
<dtml-var num> est inférieur à 5.
<dtml-else>
<dtml_var num> est égal à 5.
</dtml-if>
\end{verbatim}
l'objet \texttt{num} peut avoir été récupéré par un formulaire, par exemple. On remarque la
présence du mot clé \texttt{expr} pour spécifier une expression à la place d'un simple objet.
\subsection{Itérer une action sur des objets : balise \emph{in}}
En DTML, on peut itérer sur une séquence d'objets, par exemple pour les afficher:
\begin{verbatim}
<dtml-in objectValues>
<dtml-var getId><BR>
</dtml-in>
\end{verbatim}
\texttt{objectValues} est un mot-clé de \zope qui donne la liste des objets du \emph{Folder} courant, 
et \texttt{getId} est une méthode interne à \zope qui affiche le nom d'un objet.
\subsection{Définition d'objet : balise \emph{let}}
La balise \emph{let} permet de créer des objets simples depuis un document DTML. On s'en
sert par exemple pour créer des chaines de caractères à réutiliser:
\begin{verbatim}
<dtml-let nom="'Bob'">
<p><dtml-var nom> est sympa.</p>
</dtml-let>
\end{verbatim}
On remarquera que la portée de \emph{let} s'arrête à la balise \texttt{</dtml-let>}. On peut
se servir de \emph{let} et de \emph{in} pour parcourir des tuples ou des listes:
\begin{verbatim}
<dtml-in expr="(1,2,3,4)">
 <dtml-let num=sequence-item index=sequence-index result="num*index">
 <dtml-var num> * <dtml-var index> = <dtml-var result>
 </dtml-let>
</dtml-in>
\end{verbatim}
la variable clé \texttt{sequence-item} est l'objet courant de la séquence explorée par
\emph{in} et la variable clé \texttt{sequence-index} est l'index de l'objet courant dans 
la séquence, à partir de 0. On remarque que DTML comprend l'opérateur \texttt{*} comme
une multiplication dans le \emph{let} et comme un caractère sinon.
\subsection{Appel d'objet : balise \emph{call}}
La balise \emph{call} ressemble à la balise \emph{var} sauf qu'elle paermet de ne pas afficher
l'objet appelé. On s'en sert notament pour appeler des méthodes SQL.

On se réferera au manuel de référence DTML
\footnote{\texttt{http://www.zope.org/Members/michel/ZB/AppendixA.dtml}}
pour les autres balises et opérations DTML.
\section{Utiliser des Scripts Python}
On peut utiliser des scripts \emph{Python} ou \emph{Perl} pour effectuer des traitements de
données. Pour ajouter un script \emph{Python}, on sélectionne \texttt{[Script (Python)]} et on
clique sur \texttt{add}. 
\subsection{Un exemple simple}
Voici un script simple:
\begin{verbatim}
return (int(x) + 2)
\end{verbatim}
On a aussi pris soin de mettre \texttt{x} dans la liste des paramètres du script. On peut le
tester en cliquant sur l'onglet \emph{test} qui nous montre un formulaire pour saisir
\texttt{x}. Un script peut s'appeler par son URL ou par une balise \texttt{<dtml-call [...]>}. 
Ce qui est retourné par le script s'affiche tout simplement. 
\subsection{Un script complexe}
Voici un script plus compliqué: il permet de
poster des news. voici le formulaire d'entrée \texttt{add\_newz}:
\begin{verbatim}
<dtml-var standard_html_header>
<form action="action_add_newz" method="post">
titre : <input type="text" name="titre"><br>
texte : <textarea name="texte" rows="4" cols="50">
</textarea>
<input type="hidden" name="fdate" value="<dtml-var ZopeTime fmt="%d/%m/%Y">">
<input type="submit" value="envoyer">
</form>
<dtml-var standard_html_footer>
\end{verbatim}
Trois paramètres sont envoyés : \texttt{titre}, \texttt{texte} et \texttt{fdate}. On
remarque que fdate est générée par la méthode ZopeTime. Voici la méthode DTML
\emph{action\_add\_newz} qui récupère ces paramètres.
\begin{verbatim}
<dtml-var standard_html_header>
<dtml-call expr="addnewz(titre,texte,fdate)">
<h1> nouvelle ajoutée !</h1>
<p><a href="add_newz">retour</a></p>
<dtml-var standard_html_footer>
\end{verbatim}
Cette méthode appelle le script \texttt{addnewz} avec les bons paramètres. Voici le script
Python \texttt{addnewz}:
\begin{verbatim}
## on crée l'identifiant de la news
id='news_%d' % len(context.news.objectIds())
## on crée un objet DTML et on lui affecte la news
context.news.manage_addProduct['OFSP'].manage_addDTMLDocument(id,title=titre,file=texte)
## on prend ce document
doc=getattr(context.news,id)
## on lui ajoute la propriété date
doc.manage_addProperty('date', fdate, 'string')
\end{verbatim}
On aura pris soin de placer \texttt{titre}, \texttt{texte} et \texttt{fdate} dans le champ
\emph{Arguments} du script. Modifions maintenant le document DTML
\texttt{index\_html} pour visualiser les news:
\begin{verbatim}
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<dtml-in "news.objectValues()" size="3" start="0" sort="name" reverse>
 <hr>
 <p>
 <b><dtml-var sequence-var-date></b><dtml-var sequence-var-title><br><br>
 <dtml-var sequence-item><br>
 </p>
</dtml-in>
<hr>
<a href="add_newz">Ajouter une nouvelle</a>
<dtml-var standard_html_footer>
\end{verbatim}
La balise \texttt{<dtml-in ...>} permet de boucler sur une liste d'éléments. La méthode 
\texttt{news.objectValues()} permet de scanner le répertoire \texttt{news}.
\texttt{sequence-var-[...]} permet d'accéder aux variables de l'item en cours.
 On se réfèrera au \emph{Zope-book}
\footnote{\texttt{http://www.zope.org/Members/michel/ZB/ScriptingZope.dtml}}
pour de plus amples détails.
\section{Utiliser une base de donnnées relationnelle}
Il existe des produits \zope pour se connecter à des SGBD comme \emph{PostgreSQL},
\emph{Oracle}, \emph{MySQL} et d'autres encore. Nous allons utiliser le petit SGBD fournit
avec \zope : \gadfly. \gadfly ne supporte pas d'énormes quantités de données car il charge
toutes les données en mémoire.
\subsection{Se connecter}
Pour se connecter, on clique sur \texttt{[Select type to add]} et on selectionne 
\texttt{ZGadfly Database Connection}. On identifie la connection, on utilise le 
répertoire demo et on valide.
\subsection{créer une table}
Pour créer une table, on va dans l'onglet \emph{Test} et on entre le code SQL directement, par
exemple :\\
\begin{verbatim}
CREATE TABLE employes
(id integer, prenom varchar, nom varchar, salaire float)
\end{verbatim}
On s'assure d'avoir un identifiant unique pour \texttt{id} :\\ 
\begin{verbatim} 
CREATE UNIQUE INDEX emp-id ON employes (id)
\end{verbatim}
\subsection{créer une méthode SQL}
On crée une méthode SQL en cliquant sur \texttt{[Select type to add]} puis on selectionne
\texttt{Z SQL Method} et con clique sur \texttt{add}. Dans le champ \emph{Arguments}, 
on donne les arguments à passer à la méthode. On veut par exemple ajouter un empoyé donc
on donne les quatres arguments nécessaires : \texttt{id prenom nom salaire} puis on
écrit la requète SQL dans le \emph{Query Template}:
\begin{verbatim}
insert into employes (id, prenom, nom, salaire)
values (<dtml-sqlvar Myid type="int">, <dtml-sqlvar Myprenom type="string">, 
<dtml-sqlvar Mynom type="string">, <dtml-sqlvar Mysalaire type="float">)
\end{verbatim}
On remarque que l'on a en paramètres des variables de type \emph{sqlvar}. Ainsi, la méthode
placera nos arguments en lieu et place des balises \emph{dtml-sqlvar}. On peut tester la
méthode en allant sur l'onglet \emph{test} après avoir enregistré les modifications.

On peut créer une méthode de lecture de la même manière:
\begin{verbatim}
select * from employes where id=<dtml-var n>
\end{verbatim}
mais cette méthode n'est pas sûre car elle ne vérifie pas le type. On lui préfèrera:
\begin{verbatim}
select * from employes where id=<dtml-sqlvar n type=int>
\end{verbatim}
où \texttt{n} est le paramètre passé à la méthode. Le résultat retourné est forcément une
séquence de lignes, même si il n'y a qu'une ligne. On a donc en retour une liste d'objets lignes
qui sont éphémères, le temps de l'exécution de la méthode.
\subsection{Présenter un résultat de requète}
On crée un document DTML pour présenter les données reçues d'une requète en appelant
cette requète dont on sait qu'elle nous donnera une liste de résultats. On peut exploiter cette
liste grâce à la balise \emph{dtml-in}. Par exemple:
\begin{verbatim}
<ul>
<dtml-in MaMethodeSQL>
<li><dtml-var prenom> <dtml-var nom>
</dtml-in>
</ul>
\end{verbatim}
nous donne la liste des personnes trouvées par la requète \emph{MaMethodeSQL} créée
auparavant. L'itération dans la liste est assurée par la balise \emph{dtml-in} qui permettra
donc d'afficher une liste de personnes.
\subsection{Donner des arguments à une méthode SQL}
Pour créer une interface DTML afin de donner des arguments à une méthode SQL, on peut
utiliser le produit \texttt{Z Search Interface} qui nous fournira un modèle. On spécifie l'objet à
chercher:  la méthode \emph{MaMethodeSQL} par exemple, et on nomme deux méthodes
DTML, une pour l'entrée des données (\emph{search input Id}) et l'autre pour les résultats
(\emph{Report Id}). On choisit aussi le type de présentation. Pour simplifier la présentation des
résultats, modifions la méthode de présentation des résultats pour obtenir:
\begin{verbatim}
<dtml-var standard_html_header>
<dtml-call MaMethodeSQL>
<h1>l'employé <dtml-var prenom> est embauché !</h1>
\end{verbatim}
On peut aussi modifier la méthode de prise des arguments pour ses besoins. L'important est
qu'elle appelle la méthode de traitement (balise html \texttt{form action=\dots}) avec les bons arguments qui
seront alors dans l'espace de nommage accessible par \emph{MaMethodeSQL}.
\section{\'Etendre les fonctionnalités de \zope :\\ les Produits}
Les produits sont comme des plug-ins de \zope. Ils sont écrits en Python. On les place dans le
répertoire \texttt{Products} de zope, généralement:
\texttt{/usr/lib/zope/lib/python/Products}. On doit créer le fichier \texttt{\_\_init\_\_.py} qui 
doit importer la classe produit. On ajoute à cela une fonction d'initilisation:
\begin{verbatim}
import MaClasse 
def initialize(context): 
""" 
initialise MaClasse
"""
    context.registerClass(
        MonProduit.MaClasse,
        permission=[...], 
        construcors = (MaClasse.manage_addMonProduit, MaClasse.manage_addMonProduitForm),
        icon='MonIcone.png')
\end{verbatim}
La classe \emph{MaClasse} sera alors connue de \zope. Elle va apparaitre  dans le menu
\texttt{Select type to add}. Pour cela, dans le module \emph{MonProduit}, 
on indique comment doit appara\^\i tre le produit:
\begin{verbatim}
from OFS import SimpleItem
class MaClasse(SimpleItem.Item)
"""
exemple MaClasse
"""
    # type qui apparaitra dans le select
    meta_type = 'minimal'
    # onglets de gestion
    manage_options = ({'label':'View', 'action':'index_html'},)
    # initialisation de MaClasse
    def __init__(self,id):
        "a la creation d'un objet de MaClasse"
        self.id = id
    # vue de l'objet
    def index_html(self):
        "vue par defaut"
        return '<html><body>Salut!</body></html>'
    # administration
    def manage_addMonProduit(self, id, RESPONSE=None):
        "Ajoute un objet de MonProduit dans un folder"
        self._setObject(id, MaClasse(id))
        RESPONSE.redirect('index_html')
    def manage_addMonProduitForm(self)
        "formulaire pour obtenir l'id de l'instance"
        return """<html><body>Id de l'instance ?<br>
<form name="form" action="manage_addMonProduit">
<input type="text" name="id"><br>
<input type="submit" value="creer">
</form></body></html>"""
\end{verbatim}
Ici, MaClasse hérite de SimpleItem.Item. On ajoute aux options \texttt{manage\_options}
le label 'View' qui appelle \texttt{index\_html}, lequel affiche du texte html. On trouve les
deux méthodes qui vont sevir à la construction de l'objet: \texttt{manage\_addMonProduit}
et \texttt{manage\_addMonProduitForm} qui ont été déclarées dans
\texttt{\_\_init\_\_.py}. En conséquence, l'instance créée est une page html 
définie par \texttt{index\_html(self)}. Toute autre méthode du nouvel objet est accessible 
par son URL, par exemple: \verb#/MonId/MaMethode#.
\end{document}
