--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/fr/chap_fondements_cubicweb.txt Thu Nov 13 02:29:41 2008 +0100
@@ -0,0 +1,401 @@
+.. -*- coding: utf-8 -*-
+
+Fondements `CubicWeb`
+=====================
+
+Architecture globale
+--------------------
+.. image:: archi_globale.png
+
+**Note**: en pratique la partie cliente et la partie serveur sont
+généralement intégrées dans le même processus et communiquent donc
+directement, sans nécessiter des appels distants via Pyro. Il est
+cependant important de retenir que ces deux parties sont disjointes
+et qu'il est même possible d'en exécuter plusieurs exemplaires dans
+des processus distincts pour répartir la charge globale d'un site
+sur une ou plusieurs machines.
+
+Concepts et vocabulaire
+-----------------------
+
+*schéma*
+ le schéma définit le modèle de données d'une application sous forme
+ d'entités et de relations, grâce à la biblithèque `yams`_. C'est
+ l'élément central d'une application. Il est initialement défini sur
+ le système de fichiers et est stocké dans la base de données lors de
+ la création d'une instance. `CubicWeb` fournit un certain nombres de
+ types d'entités inclus systématiquement car nécessaire au noyau
+ `CubicWeb` et une librairie de cubes devant être inclus
+ explicitement le cas échéant.
+
+*type d'entité*
+ une entité est un ensemble d'attributs ; l'attribut de
+ base de toute entité, qui est sa clef, est l'eid
+
+*type de relation*
+ les entités sont liées entre elles par des relations. Dans `CubicWeb`
+ les relations sont binaires : par convention on nomme le premier terme
+ d'une relation son 'sujet' et le second son 'objet'.
+
+*type d'entité final*
+ les types finaux correspondent aux types de bases comme les chaînes
+ de caractères, les nombres entiers... Une propriété de ces types est
+ qu'ils ne peuvent être utilisés qu'uniquement comme objet d'une
+ relation. Les attributs d'une entité (non finale) sont des entités
+ (finales).
+
+*type de relation finale*
+ une relation est dite finale si son objet est un type final. Cela revient à
+ un attribut d'une entité.
+
+*entrepôt*
+ ou *repository*, c'est la partie serveur RQL de `CubicWeb`. Attention à ne pas
+ confondre avec un entrepôt mercurial ou encore un entrepôt debian.
+
+*source*
+ une source de données est un conteneur de données quelquonque (SGBD, annuaire
+ LDAP...) intégré par l'entrepôt `CubicWeb`. Un entrepôt possède au moins une source
+ dite "system" contenant le schéma de l'application, l'index plein-texte et
+ d'autres informations vitales au système.
+
+*configuration*
+ il est possible de créer différentes configurations pour une instance :
+
+ - ``repository`` : entrepôt uniquement, accessible pour les clients via Pyro
+ - ``twisted`` : interface web uniquement, communiquant avec un entrepôt via Pyro
+ - ``all-in-one`` : interface web et entrepôt dans un seul processus. L'entrepôt
+ peut ou non être accessible via Pyro
+
+*cube*
+ un cube est un modèle regroupant un ou plusieurs types de données et/ou
+ des vues afin de fournir une fonctionalité précise, ou une application `CubicWeb`
+ complète utilisant éventuellement d'autres cube. Les différents
+ cubes disponibles sur une machine sont installés dans
+ `/path/to/forest/cubicweb/cubes`
+
+*instance*
+ une instance est une installation spécifique d'un template. Sont regroupes
+ dans une instance tous les fichiers de configurations necessaires au bon
+ fonctionnement de votre application web. Elle referrera au(x) cube(s) sur
+ le(s)quel(s) votre application se base.
+ Par exemple intranet/jpl et logilab.org sont deux instances du cube jpl que
+ nous avons developpes en interne.
+ Les instances sont définies dans le répertoire `~/etc/cubicweb.d`.
+
+*application*
+ le mot application est utilisé parfois pour parler d'une instance et parfois
+ d'un composant, en fonction du contexte... Mieux vaut donc éviter de
+ l'utiliser et utiliser plutôt *cube* et *instance*.
+
+*result set*
+ objet encaspulant les résultats d'une requête RQL et des informations sur
+ cette requête.
+
+*Pyro*
+ `Python Remote Object`_, système d'objets distribués pur Python similaire à
+ Java's RMI (Remote Method Invocation), pouvant être utilisé pour la
+ communication entre la partie web du framework et l'entrepôt RQL.
+
+.. _`Python Remote Object`: http://pyro.sourceforge.net/
+.. _`yams`: http://www.logilab.org/project/name/yams/
+
+
+Moteur `CubicWeb`
+-----------------
+
+Le moteur web de `CubicWeb` consiste en quelques classes gérant un ensemble d'objets
+chargés dynamiquement au lancement de `CubicWeb`. Ce sont ces objets dynamiques, issus
+du modèle ou de la librairie, qui construisent le site web final. Les différents
+composants dynamiques sont par exemple :
+
+* coté client et serveur
+
+ - les définitions d'entités, contenant la logique permettant la manipulation des
+ données de l'application
+
+* coté client
+
+ - les *vues* , ou encore plus spécifiquement
+
+ - les boites
+ - l'en-tête et le pied de page
+ - les formulaires
+ - les gabarits de pages
+
+ - les *actions*
+ - les *controleurs*
+
+* coté serveur
+
+ - les crochets de notification
+ - les vues de notification
+
+Les différents composants du moteur sont :
+
+* un frontal web (seul twisted disponible pour le moment), transparent du point
+ de vue des objets dynamiques
+* un objet encapsulant la configuration
+* un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements
+
+Détail de la procédure d'enregistrement
+---------------------------------------
+Au démarage le `vregistry` ou base de registres inspecte un certain nombre de
+répertoires à la recherche de définition de classes "compatible". Après une
+procédure d'enregistrement les objets sont affectés dans différents registres
+afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de
+l'application.
+
+La classe de base de tout ces objets est la classe `AppRsetObject` (module
+`cubicweb.common.appobject`).
+
+
+API Python/RQL
+--------------
+
+Inspiré de la db-api standard, avec un object Connection possédant les méthodes
+cursor, rollback et commit principalement. La méthode importante est la méthode
+`execute` du curseur :
+
+`execute(rqlstring, args=None, eid_key=None, build_descr=True)`
+
+:rqlstring: la requête rql à éxécuter (unicode)
+:args: si la requête contient des substitutions, un dictionnaire contenant les
+ valeurs à utiliser
+:eid_key:
+ un détail d'implémentation du cache de requêtes RQL fait que si une substitution est
+ utilisée pour introduire un eid *levant des ambiguités dans la résolution de
+ type de la requête*, il faut spécifier par cet argument la clé correspondante
+ dans le dictionnaire
+
+C'est l'objet Connection qui possède les méthodes classiques `commit` et
+`rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement
+d'interface web sur la base du framework `CubicWeb` étant donné que la fin de la
+transaction est déterminée par celui-ci en fonction du succès d'éxécution de la
+requête.
+
+NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une
+requête génère une erreur liée à la sécurité, un rollback est systématiquement
+effectuée sur la transaction courante.
+
+
+La classe `Request` (`cubicweb.web`)
+------------------------------------
+Une instance de requête est créée lorsque une requête HTTP est transmise au
+serveur web. Elle contient des informations telles que les paramètres de
+formulaires, l'utilisateur connecté, etc.
+
+**De manière plus générale une requête représente une demande d'un utilisateur,
+que se soit par HTTP ou non (on parle également de requête rql coté serveur par
+exemple)**
+
+Une instance de la classe `Request` possède les attributs :
+
+* `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur
+ connecté
+* `form`, dictionaire contenant les valeurs de formulaire web
+* `encoding`, l'encodage de caractère à utiliser dans la réponse
+
+Mais encore :
+
+:Gestion des données de session:
+ * `session_data()`, retourne un dictionaire contenant l'intégralité des
+ données de la session
+ * `get_session_data(key, default=None)`, retourne la valeur associée à
+ la clé ou la valeur `default` si la clé n'est pas définie
+ * `set_session_data(key, value)`, associe une valeur à une clé
+ * `del_session_data(key)`, supprime la valeur associé à une clé
+
+
+:Gestion de cookie:
+ * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête
+ HTTP 'Cookie'
+ * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`,
+ avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke
+ *de session"* expirant quand l'utilisateur ferme son navigateur
+ * `remove_cookie(cookie, key)`, fait expirer une valeur
+
+:Gestion d'URL:
+ * `url()`, retourne l'url complète de la requête HTTP
+ * `base_url()`, retourne l'url de la racine de l'application
+ * `relative_path()`, retourne chemin relatif de la requête
+
+:Et encore...:
+ * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP
+ 'Content-Type'
+ * `get_header(header)`, retourne la valeur associé à un en-tête HTTP
+ arbitraire de la requête
+ * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la
+ réponse
+ * `cursor()` retourne un curseur RQL sur la session
+ * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
+ * `property_value(key)`, gestion des propriétés (`EProperty`)
+ * le dictionaire `data` pour stocker des données pour partager de
+ l'information entre les composants *durant l'éxécution de la requête*.
+
+A noter que cette classe est en réalité abstraite et qu'une implémentation
+concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted*
+aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur,
+la majeure partie de l'interface de `Request` est définie sur la session
+associée au client.
+
+
+La classe `AppObject`
+---------------------
+
+En général :
+
+* on n'hérite pas directement des cette classe mais plutôt d'une classe
+ plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`,
+ `Action`...
+
+* pour être enregistrable, un classe fille doit définir son registre (attribut
+ `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à
+ s'occuper du registre, uniquement de l'identifiant `id` :)
+
+On trouve un certain nombre d'attributs et de méthodes définis dans cette classe
+et donc commune à tous les objets de l'application :
+
+A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux
+*classes* filles:
+
+* `vreg`, le `vregistry` de l'application
+* `schema`, le schéma de l'application
+* `config`, la configuration de l'application
+
+On trouve également sur les instances les attributs :
+
+* `req`, instance de `Request`
+* `rset`, le "result set" associé à l'objet le cas échéant
+* `cursor`, curseur rql sur la session
+
+
+:Gestion d'URL:
+ * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à
+ partir des arguments donnés. Le *controleur* devant gérer la réponse
+ peut-être spécifié via l'argument spécial `method` (le branchement est
+ théoriquement bien effectué automatiquement :).
+
+ * `datadir_url()`, retourne l'url du répertoire de données de l'application
+ (contenant les fichiers statiques tels que les images, css, js...)
+
+ * `base_url()`, raccourci sur `req.base_url()`
+
+ * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote`
+
+:Manipulation de données:
+
+ * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()`
+
+ * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour
+ l'eid donné
+ * `entity(row, col=0)`, retourne l'entité correspondant à la position données
+ du "result set" associé à l'objet
+
+ * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais
+ appelle également la méthode `complete()` sur l'entité avant de la retourner
+
+:Formattage de données:
+ * `format_date(date, date_format=None, time=False)`
+ * `format_time(time)`,
+
+:Et encore...:
+
+ * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans
+ le fichier de configuration `external_resource`
+
+ * `tal_render(template, variables)`,
+
+
+**NOTE IMPORTANTE**
+Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours**
+utiliser **super()** pour récupérer les méthodes et attributs des classes
+parentes, et pas passer par l'identifiant de classe parente directement.
+(sous peine de tomber sur des bugs bizarres lors du rechargement automatique
+des vues). Par exemple, plutôt que d'écrire::
+
+ class Truc(PrimaryView):
+ def f(self, arg1):
+ PrimaryView.f(self, arg1)
+
+Il faut écrire::
+
+ class Truc(PrimaryView):
+ def f(self, arg1):
+ super(Truc, self).f(arg1)
+
+
+
+
+
+
+Structure standard d'un cube
+----------------------------
+
+Un cube complexe est structuré selon le modèle suivant :
+
+::
+
+ moncube/
+ |
+ |-- schema.py
+ |
+ |-- entities/
+ |
+ |-- sobjects/
+ |
+ |-- views/
+ |
+ |-- test/
+ |
+ |-- i18n/
+ |
+ |-- data/
+ |
+ |-- migration/
+ | |- postcreate.py
+ | \- depends.map
+ |
+ |-- debian/
+ |
+ \-- __pkginfo__.py
+
+On peut utiliser de simple module python plutôt que des répertoires (packages),
+par ex.:
+
+::
+
+ moncube/
+ |
+ |-- entities.py
+ |-- hooks.py
+ \-- views.py
+
+
+où :
+
+* ``schema`` contient la définition du schéma (coté serveur uniquement)
+* ``entities`` contient les définitions d'entités (coté serveur et interface web)
+* ``sobjects`` contient les crochets et/ou vues de notification (coté serveur
+ uniquement)
+* ``views`` contient les différents composants de l'interface web (coté interface
+ web uniquement)
+* ``test`` contient les tests spécifiques à l'application (non installé)
+* ``i18n`` contient les catalogues de messages pour les langues supportées (coté
+ serveur et interface web)
+* ``data`` contient des fichiers de données arbitraires servis statiquement
+ (images, css, fichiers javascripts)... (coté interface web uniquement)
+* ``migration`` contient le fichier d'initialisation de nouvelles instances
+ (``postcreate.py``) et générallement un fichier donnant les dépendances `CubicWeb` du
+ composant en fonction de la version de celui-ci (``depends.map``)
+* ``debian`` contient les fichiers contrôlant le packaging debian (vous y
+ trouverez les fichiers classiques ``control``, ``rules``, ``changelog``... (non
+ installé)
+* le fichier ``__pkginfo__.py`` donne un certain nombre de méta-données sur le
+ composant, notamment le nom de la distribution et la version courante (coté
+ serveur et interface web) ou encore les sous-cubes utilisés par ce
+ cube.
+
+Le strict minimum étant :
+
+* le fichier ``__pkginfo__.py``
+* la définition du schéma