doc/book/en/02-foundation.en.txt
changeset 93 9c919a47e140
child 96 c1d04b2fa8c6
equal deleted inserted replaced
92:30f19b976857 93:9c919a47e140
       
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 Fondements `CubicWeb`
       
     4 =====================
       
     5 Un peu d'histoire...
       
     6 --------------------
       
     7 
       
     8 `CubicWeb` est une plate-forme logicielle de développement d'application web
       
     9 qui est développée par Logilab_ depuis 2001. 
       
    10 
       
    11 
       
    12 Entièrement développée en Python, `CubicWeb` publie des données provenant
       
    13 de plusieurs sources telles que des bases de données SQL, des répertoire 
       
    14 LDAP et des systèmes de gestion de versions tels que subversion. 
       
    15 
       
    16 L'interface utilisateur de `CubicWeb` a été spécialement conçue pour laisser 
       
    17 à l'utilisateur final toute latitude pour sélectionner puis présenter les données. 
       
    18 Elle permet d'explorer aisément la base de connaissances et d'afficher les 
       
    19 résultats avec la présentation la mieux adaptée à la tâche en cours. 
       
    20 La flexibilité de cette interface redonne à l'utilisateur le contrôle de 
       
    21 paramètres d'affichage et de présentation qui sont habituellement réservés 
       
    22 aux développeurs.
       
    23 
       
    24 Parmi les applications déjà réalisées, on dénombre un annuaire en ligne pour 
       
    25 le grand public (voir http://www.118000.fr/), un système de gestion d'études 
       
    26 numériques et de simulations pour un bureau d'études, un service de garde 
       
    27 partagée d'enfants (voir http://garde-partagee.atoukontact.fr/), la gestion 
       
    28 du développement de projets logiciels (voir http://www.logilab.org), etc.
       
    29 
       
    30 En 2008, `CubicWeb` a été porté pour un nouveau type de source: le datastore 
       
    31 de GoogleAppEngine_.
       
    32 
       
    33 .. _GoogleAppEngine: http://code.google.com/appengine/
       
    34 
       
    35 
       
    36 Architecture globale
       
    37 --------------------
       
    38 .. image:: images/archi_globale.png
       
    39 
       
    40 **Note**: en pratique la partie cliente et la partie serveur sont
       
    41 généralement intégrées dans le même processus et communiquent donc
       
    42 directement, sans nécessiter des appels distants via Pyro. Il est
       
    43 cependant important de retenir que ces deux parties sont disjointes
       
    44 et qu'il est même possible d'en exécuter plusieurs exemplaires dans
       
    45 des processus distincts pour répartir la charge globale d'un site
       
    46 sur une ou plusieurs machines.
       
    47 
       
    48 Concepts et vocabulaire
       
    49 -----------------------
       
    50 
       
    51 *schéma*
       
    52   le schéma définit le modèle de données d'une application sous forme
       
    53   d'entités et de relations, grâce à la bibliothèque `yams`_. C'est
       
    54   l'élément central d'une application. Il est initialement défini sur
       
    55   le système de fichiers et est stocké dans la base de données lors de
       
    56   la création d'une instance. `CubicWeb` fournit un certain nombres de
       
    57   types d'entités inclus systématiquement car nécessaire au noyau
       
    58   `CubicWeb` et une librairie de cubes devant être inclus
       
    59   explicitement le cas échéant.
       
    60 
       
    61 *type d'entité* 
       
    62   une entité est un ensemble d'attributs ; l'attribut de
       
    63   base de toute entité, qui est sa clef, est l'eid
       
    64 
       
    65 *type de relation*
       
    66   les entités sont liées entre elles par des relations. Dans `CubicWeb` 
       
    67   les relations sont binaires : par convention on nomme le premier terme
       
    68   d'une relation son 'sujet' et le second son 'objet'.
       
    69 
       
    70 *type d'entité final*
       
    71   les types finaux correspondent aux types de bases comme les chaînes
       
    72   de caractères, les nombres entiers... Une propriété de ces types est
       
    73   qu'ils ne peuvent être utilisés qu'uniquement comme objet d'une
       
    74   relation. Les attributs d'une entité (non finale) sont des entités
       
    75   (finales).
       
    76 
       
    77 *type de relation finale*
       
    78   une relation est dite finale si son objet est un type final. Cela revient à
       
    79   un attribut d'une entité.
       
    80 
       
    81 *entrepôt*
       
    82   ou *repository*, c'est la partie serveur RQL de `CubicWeb`. Attention à ne pas
       
    83   confondre avec un entrepôt mercurial ou encore un entrepôt debian.
       
    84 
       
    85 *source*
       
    86   une source de données est un conteneur de données quelquonque (SGBD, annuaire
       
    87   LDAP...) intégré par l'entrepôt `CubicWeb`. Un entrepôt possède au moins une source
       
    88   dite "system" contenant le schéma de l'application, l'index plein-texte et
       
    89   d'autres informations vitales au système.
       
    90 
       
    91 *configuration*
       
    92   il est possible de créer différentes configurations pour une instance :
       
    93 
       
    94   - ``repository`` : entrepôt uniquement, accessible pour les clients via Pyro
       
    95   - ``twisted`` : interface web uniquement, communiquant avec un entrepôt via Pyro
       
    96   - ``all-in-one`` : interface web et entrepôt dans un seul processus. L'entrepôt
       
    97      peut ou non être accessible via Pyro
       
    98 
       
    99 *cube*
       
   100   un cube est un modèle regroupant un ou plusieurs types de données et/ou
       
   101   des vues afin de fournir une fonctionalité précise, ou une application `CubicWeb`
       
   102   complète utilisant éventuellement d'autres cube. Les différents
       
   103   cubes disponibles sur une machine sont installés dans
       
   104   `/path/to/forest/cubicweb/cubes`
       
   105 
       
   106 *instance*
       
   107   une instance est une installation spécifique d'un cube. Sont regroupes
       
   108   dans une instance tous les fichiers de configurations necessaires au bon
       
   109   fonctionnement de votre application web. Elle referrera au(x) cube(s) sur 
       
   110   le(s)quel(s) votre application se base.
       
   111   Par exemple intranet/jpl et logilab.org sont deux instances du cube jpl que
       
   112   nous avons developpes en interne. 
       
   113   Les instances sont définies dans le répertoire `~/etc/cubicweb.d`.
       
   114 
       
   115 *application*
       
   116   le mot application est utilisé parfois pour parler d'une instance et parfois
       
   117   d'un composant, en fonction du contexte... Mieux vaut donc éviter de
       
   118   l'utiliser et utiliser plutôt *cube* et *instance*.
       
   119 
       
   120 *result set*
       
   121   objet encaspulant les résultats d'une requête RQL et des informations sur
       
   122   cette requête.
       
   123 
       
   124 *Pyro*
       
   125   `Python Remote Object`_, système d'objets distribués pur Python similaire à
       
   126   Java's RMI (Remote Method Invocation), pouvant être utilisé pour la
       
   127   communication entre la partie web du framework et l'entrepôt RQL.
       
   128 
       
   129 .. _`Python Remote Object`: http://pyro.sourceforge.net/
       
   130 .. _`yams`: http://www.logilab.org/project/name/yams/
       
   131 
       
   132 
       
   133 Moteur `CubicWeb`
       
   134 -----------------
       
   135 
       
   136 Le moteur web de `CubicWeb` consiste en quelques classes gérant un ensemble d'objets
       
   137 chargés dynamiquement au lancement de `CubicWeb`. Ce sont ces objets dynamiques, issus
       
   138 du modèle ou de la librairie, qui construisent le site web final. Les différents
       
   139 composants dynamiques sont par exemple : 
       
   140 
       
   141 * coté client et serveur
       
   142 
       
   143  - les définitions d'entités, contenant la logique permettant la manipulation des
       
   144    données de l'application
       
   145 
       
   146 * coté client
       
   147 
       
   148   - les *vues* , ou encore plus spécifiquement 
       
   149 
       
   150     - les boites
       
   151     - l'en-tête et le pied de page
       
   152     - les formulaires
       
   153     - les gabarits de pages
       
   154 
       
   155   - les *actions*
       
   156   - les *controleurs*
       
   157 
       
   158 * coté serveur
       
   159 
       
   160   - les crochets de notification
       
   161   - les vues de notification
       
   162 
       
   163 Les différents composants du moteur sont :
       
   164 
       
   165 * un frontal web (seul twisted disponible pour le moment), transparent du point
       
   166   de vue des objets dynamiques
       
   167 * un objet encapsulant la configuration
       
   168 * un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements
       
   169 
       
   170 Détail de la procédure d'enregistrement
       
   171 ---------------------------------------
       
   172 Au démarage le `vregistry` ou base de registres inspecte un certain nombre de
       
   173 répertoires à la recherche de définition de classes "compatible". Après une
       
   174 procédure d'enregistrement les objets sont affectés dans différents registres
       
   175 afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de
       
   176 l'application.
       
   177 
       
   178 La classe de base de tout ces objets est la classe `AppRsetObject` (module
       
   179 `cubicweb.common.appobject`). 
       
   180 
       
   181 
       
   182 API Python/RQL
       
   183 --------------
       
   184 
       
   185 Inspiré de la db-api standard, avec un object Connection possédant les méthodes
       
   186 cursor, rollback et commit principalement. La méthode importante est la méthode
       
   187 `execute` du curseur :
       
   188 
       
   189 `execute(rqlstring, args=None, eid_key=None, build_descr=True)`
       
   190 
       
   191 :rqlstring: la requête rql à éxécuter (unicode)
       
   192 :args: si la requête contient des substitutions, un dictionnaire contenant les
       
   193        valeurs à utiliser
       
   194 :eid_key: 
       
   195    un détail d'implémentation du cache de requêtes RQL fait que si une substitution est
       
   196    utilisée pour introduire un eid *levant des ambiguités dans la résolution de
       
   197    type de la requête*, il faut spécifier par cet argument la clé correspondante
       
   198    dans le dictionnaire
       
   199 
       
   200 C'est l'objet Connection qui possède les méthodes classiques `commit` et
       
   201 `rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement
       
   202 d'interface web sur la base du framework `CubicWeb` étant donné que la fin de la
       
   203 transaction est déterminée par celui-ci en fonction du succès d'éxécution de la
       
   204 requête. 
       
   205 
       
   206 NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une
       
   207 requête génère une erreur liée à la sécurité, un rollback est systématiquement
       
   208 effectuée sur la transaction courante.
       
   209 
       
   210 
       
   211 La classe `Request` (`cubicweb.web`)
       
   212 ------------------------------------
       
   213 Une instance de requête est créée lorsque une requête HTTP est transmise au
       
   214 serveur web. Elle contient des informations telles que les paramètres de
       
   215 formulaires, l'utilisateur connecté, etc. 
       
   216 
       
   217 **De manière plus générale une requête représente une demande d'un utilisateur,
       
   218 que se soit par HTTP ou non (on parle également de requête rql coté serveur par
       
   219 exemple)**
       
   220 
       
   221 Une instance de la classe `Request` possède les attributs :
       
   222 
       
   223 * `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur
       
   224   connecté 
       
   225 * `form`, dictionaire contenant les valeurs de formulaire web
       
   226 * `encoding`, l'encodage de caractère à utiliser dans la réponse
       
   227 
       
   228 Mais encore :
       
   229 
       
   230 :Gestion des données de session:        
       
   231   * `session_data()`, retourne un dictionaire contenant l'intégralité des
       
   232     données de la session
       
   233   * `get_session_data(key, default=None)`, retourne la valeur associée à
       
   234     la clé ou la valeur `default` si la clé n'est pas définie
       
   235   * `set_session_data(key, value)`, associe une valeur à une clé
       
   236   * `del_session_data(key)`,  supprime la valeur associé à une clé
       
   237     
       
   238 
       
   239 :Gestion de cookie:
       
   240   * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête
       
   241     HTTP 'Cookie'
       
   242   * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`,
       
   243     avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke
       
   244     *de session"* expirant quand l'utilisateur ferme son navigateur
       
   245   * `remove_cookie(cookie, key)`, fait expirer une valeur
       
   246 
       
   247 :Gestion d'URL:
       
   248   * `url()`, retourne l'url complète de la requête HTTP
       
   249   * `base_url()`, retourne l'url de la racine de l'application
       
   250   * `relative_path()`, retourne chemin relatif de la requête
       
   251 
       
   252 :Et encore...:
       
   253   * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP
       
   254     'Content-Type'
       
   255   * `get_header(header)`, retourne la valeur associé à un en-tête HTTP
       
   256     arbitraire de la requête
       
   257   * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la
       
   258     réponse 
       
   259   * `cursor()` retourne un curseur RQL sur la session
       
   260   * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
       
   261   * `property_value(key)`, gestion des propriétés (`EProperty`)
       
   262   * le dictionaire `data` pour stocker des données pour partager de
       
   263     l'information entre les composants *durant l'éxécution de la requête*.
       
   264 
       
   265 A noter que cette classe est en réalité abstraite et qu'une implémentation
       
   266 concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted*
       
   267 aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur,
       
   268 la majeure partie de l'interface de `Request` est définie sur la session
       
   269 associée au client. 
       
   270 
       
   271 
       
   272 La classe `AppObject`
       
   273 ---------------------
       
   274 
       
   275 En général :
       
   276 
       
   277 * on n'hérite pas directement des cette classe mais plutôt d'une classe
       
   278   plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`,
       
   279   `Action`...
       
   280 
       
   281 * pour être enregistrable, un classe fille doit définir son registre (attribut
       
   282   `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à
       
   283   s'occuper du registre, uniquement de l'identifiant `id` :) 
       
   284 
       
   285 On trouve un certain nombre d'attributs et de méthodes définis dans cette classe
       
   286 et donc commune à tous les objets de l'application :
       
   287 
       
   288 A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux
       
   289 *classes* filles:
       
   290 
       
   291 * `vreg`, le `vregistry` de l'application
       
   292 * `schema`, le schéma de l'application
       
   293 * `config`, la configuration de l'application
       
   294 
       
   295 On trouve également sur les instances les attributs :
       
   296 
       
   297 * `req`, instance de `Request`
       
   298 * `rset`, le "result set" associé à l'objet le cas échéant
       
   299 * `cursor`, curseur rql sur la session
       
   300 
       
   301 
       
   302 :Gestion d'URL:
       
   303   * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à
       
   304     partir des arguments donnés. Le *controleur* devant gérer la réponse
       
   305     peut-être spécifié via l'argument spécial `method` (le branchement est
       
   306     théoriquement bien effectué automatiquement :).
       
   307 
       
   308   * `datadir_url()`, retourne l'url du répertoire de données de l'application
       
   309     (contenant les fichiers statiques tels que les images, css, js...)
       
   310 
       
   311   * `base_url()`, raccourci sur `req.base_url()`
       
   312 
       
   313   * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote`
       
   314 
       
   315 :Manipulation de données:
       
   316 
       
   317   * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()`
       
   318 
       
   319   * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour
       
   320     l'eid donné
       
   321   * `entity(row, col=0)`, retourne l'entité correspondant à la position données
       
   322     du "result set" associé à l'objet
       
   323 
       
   324   * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais
       
   325     appelle également la méthode `complete()` sur l'entité avant de la retourner
       
   326 
       
   327 :Formattage de données:
       
   328   * `format_date(date, date_format=None, time=False)`
       
   329   * `format_time(time)`,
       
   330 
       
   331 :Et encore...:
       
   332 
       
   333   * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans
       
   334     le fichier de configuration `external_resource`
       
   335     
       
   336   * `tal_render(template, variables)`, 
       
   337 
       
   338 
       
   339 **NOTE IMPORTANTE**
       
   340 Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours**
       
   341 utiliser **super()** pour récupérer les méthodes et attributs des classes
       
   342 parentes, et pas passer par l'identifiant de classe parente directement.
       
   343 (sous peine de tomber sur des bugs bizarres lors du rechargement automatique
       
   344 des vues). Par exemple, plutôt que d'écrire::
       
   345 
       
   346       class Truc(PrimaryView):
       
   347           def f(self, arg1):
       
   348               PrimaryView.f(self, arg1)
       
   349 
       
   350 Il faut écrire::
       
   351       
       
   352       class Truc(PrimaryView):
       
   353           def f(self, arg1):
       
   354               super(Truc, self).f(arg1)
       
   355 
       
   356 
       
   357 
       
   358 
       
   359 
       
   360 
       
   361 Structure standard d'un cube
       
   362 ----------------------------
       
   363 
       
   364 Un cube complexe est structuré selon le modèle suivant :
       
   365 
       
   366 ::
       
   367   
       
   368   moncube/
       
   369   |
       
   370   |-- schema.py
       
   371   |
       
   372   |-- entities/
       
   373   |
       
   374   |-- sobjects/
       
   375   |
       
   376   |-- views/
       
   377   |
       
   378   |-- test/
       
   379   |
       
   380   |-- i18n/
       
   381   |
       
   382   |-- data/
       
   383   |
       
   384   |-- migration/
       
   385   | |- postcreate.py
       
   386   | \- depends.map
       
   387   |
       
   388   |-- debian/
       
   389   |
       
   390   \-- __pkginfo__.py
       
   391     
       
   392 On peut utiliser de simple module python plutôt que des répertoires (packages),
       
   393 par ex.:
       
   394 
       
   395 ::
       
   396   
       
   397   moncube/
       
   398   |
       
   399   |-- entities.py
       
   400   |-- hooks.py
       
   401   \-- views.py
       
   402     
       
   403 
       
   404 où :
       
   405 
       
   406 * ``schema`` contient la définition du schéma (coté serveur uniquement)
       
   407 * ``entities`` contient les définitions d'entités (coté serveur et interface web)
       
   408 * ``sobjects`` contient les crochets et/ou vues de notification (coté serveur
       
   409   uniquement) 
       
   410 * ``views`` contient les différents composants de l'interface web (coté interface
       
   411   web uniquement)  
       
   412 * ``test`` contient les tests spécifiques à l'application (non installé)
       
   413 * ``i18n`` contient les catalogues de messages pour les langues supportées (coté
       
   414   serveur et interface web) 
       
   415 * ``data`` contient des fichiers de données arbitraires servis statiquement
       
   416   (images, css, fichiers javascripts)... (coté interface web uniquement)
       
   417 * ``migration`` contient le fichier d'initialisation de nouvelles instances
       
   418   (``postcreate.py``) et générallement un fichier donnant les dépendances `CubicWeb` du
       
   419   composant en fonction de la version de celui-ci (``depends.map``)
       
   420 * ``debian`` contient les fichiers contrôlant le packaging debian (vous y
       
   421   trouverez les fichiers classiques ``control``, ``rules``, ``changelog``... (non
       
   422   installé) 
       
   423 * le fichier ``__pkginfo__.py`` donne un certain nombre de méta-données sur le
       
   424   composant, notamment le nom de la distribution et la version courante (coté
       
   425   serveur et interface web) ou encore les sous-cubes utilisés par ce
       
   426   cube. 
       
   427 
       
   428 Le strict minimum étant :
       
   429 
       
   430 * le fichier ``__pkginfo__.py``
       
   431 * la définition du schéma