doc/book/en/02-foundation.en.txt
changeset 93 9c919a47e140
child 96 c1d04b2fa8c6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/02-foundation.en.txt	Tue Nov 18 01:16:30 2008 +0100
@@ -0,0 +1,431 @@
+.. -*- coding: utf-8 -*-
+
+Fondements `CubicWeb`
+=====================
+Un peu d'histoire...
+--------------------
+
+`CubicWeb` est une plate-forme logicielle de développement d'application web
+qui est développée par Logilab_ depuis 2001. 
+
+
+Entièrement développée en Python, `CubicWeb` publie des données provenant
+de plusieurs sources telles que des bases de données SQL, des répertoire 
+LDAP et des systèmes de gestion de versions tels que subversion. 
+
+L'interface utilisateur de `CubicWeb` a été spécialement conçue pour laisser 
+à l'utilisateur final toute latitude pour sélectionner puis présenter les données. 
+Elle permet d'explorer aisément la base de connaissances et d'afficher les 
+résultats avec la présentation la mieux adaptée à la tâche en cours. 
+La flexibilité de cette interface redonne à l'utilisateur le contrôle de 
+paramètres d'affichage et de présentation qui sont habituellement réservés 
+aux développeurs.
+
+Parmi les applications déjà réalisées, on dénombre un annuaire en ligne pour 
+le grand public (voir http://www.118000.fr/), un système de gestion d'études 
+numériques et de simulations pour un bureau d'études, un service de garde 
+partagée d'enfants (voir http://garde-partagee.atoukontact.fr/), la gestion 
+du développement de projets logiciels (voir http://www.logilab.org), etc.
+
+En 2008, `CubicWeb` a été porté pour un nouveau type de source: le datastore 
+de GoogleAppEngine_.
+
+.. _GoogleAppEngine: http://code.google.com/appengine/
+
+
+Architecture globale
+--------------------
+.. image:: images/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 bibliothè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 cube. 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