diff -r c243c0f65f17 -r 2b3fa6fb647b doc/book/fr/02-foundation.fr.txt --- a/doc/book/fr/02-foundation.fr.txt Thu Oct 14 00:01:04 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,431 +0,0 @@ -.. -*- 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 (`CWProperty`) - * 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