.. -*- coding: utf-8 -*-
Concepts
--------
This section aims to provide you the keys of success with `CubicWeb`
by clarifying the terms specific to our framework.
Global architecture
~~~~~~~~~~~~~~~~~~~
.. image:: images/archi_globale.en.png
`CubicWeb` framework is a server/client application framework. Those two
parties communicates through RQL (`CubicWeb` query language implementation)
and ResultSet (which will be explained in :ref:`TermsVocabulary`).
The server manages all interactions with sources.
.. note::
For real, the client and server sides are integrated in the same
process and interact directly, without the needs for distants
calls using Pyro. It is important to note down that those two
sides, client/server, are disjointed and it is possible to execute
a couple of calls in distincts processes to balance the load of
your web site on one or more machines.
.. _TermsVocabulary:
Terms and vocabulary
~~~~~~~~~~~~~~~~~~~~~
`CubicWeb` defines its own terminology. To make sure there is no confusion
while reading this book, we strongly recommand you take time to go through
the following definitions that are the basics to understand while
developing with `CubicWeb`.
*schema*
The schema defines the data model of an application based on entities
and relations, modeled with a comprehensive language made of Python
classes based on `yams`_ library. This is the core piece
of an application. It is initially defined in the file system and is
stored in the database at the time an instance is created. `CubicWeb`
provides a certain number of system entities included automatically as
it is necessary for the core of `CubicWeb` and a library of
cubes (which defined application entities) that can be explicitely
included if necessary.
*entity type*
An entity type is a set of attributes; the essential attribute of
an entity is its key, named eid.
*relation type*
Entities are linked to each others by relations. In `CubicWeb`
relations are binary: by convention we name the first item of
a relation the `subject` and the second the `object`.
*final entity type*
Final types corresponds to the basic types such as string of characters,
integers... Those types have a main property which is that they can
only be used as `object` of a relation. The attributes of an entity
(non final) are entities (finals).
*final relation type*
A relation is said final if its `object` is a final type. This is equivalent
to an entity attribute.
*relation definition*
A relation definition is a 3-uple (subject entity type, relation type, object
entity type), with an associated set of property such as cardinality, constraints...
*repository*
This is the RQL server side of `CubicWeb`. Be carefull not to get
confused with a Mercurial repository or a debian repository.
*source*
A data source is a container of data (SGBD, LDAP directory, `Google
App Engine`'s datastore ...) integrated in the
`CubicWeb` repository. This repository has at least one source, `system` which
contains the schema of the application, plain-text index and others
vital informations for the system.
*configuration*
It is possible to create differents configurations for an instance:
- ``repository`` : repository only, accessible for clients using Pyro
- ``twisted`` : web interface only, access the repository using Pyro
- ``all-in-one`` : web interface and repository in a single process.
The repository could be or not accessible using Pyro.
*cube*
A cube is a model grouping one or multiple data types and/or views
to provide a specific functionnality or a complete `CubicWeb` application
potentially using other cubes. The available cubes are located in the file
system at `/path/to/forest/cubicweb/cubes` for a Mercurial forest installation,
for a debian packages installation they will be located in
`/usr/share/cubicweb/cubes`.
Larger applications can be built faster by importing cubes,
adding entities and relationships and overriding the
views that need to display or edit informations not provided by
cubes.
*instance*
An instance is a specific installation of one or multiple cubes. All the required
configuration files necessary for the well being of your web application
are grouped in an instance. This will refer to the cube(s) your application
is based on.
For example logilab.org and our intranet are two instances of a single
cube jpl, developped internally.
The instances are defined in the directory `/etc/cubicweb.d`.
*application*
The term application is sometime used to talk about an instance
and sometimes to talk of a cube depending on the context.
So we would like to avoid using this term and try to use *cube* and
*instance* instead.
*result set*
This object contains the results of an RQL query sent to the source
and informations on the query.
*Pyro*
`Python Remote Object`_, distributed objects system similar to Java's RMI
(Remote Method Invocation), which can be used for the dialog between the web
side of the framework and the RQL repository.
*query language*
A full-blown query language named RQL is used to formulate requests
to the database or any sources such as LDAP or `Google App Engine`'s
datastore.
*views*
A view is applied to a `result set` to present it as HTML, XML,
JSON, CSV, etc. Views are implemented as Python classes. There is no
templating language.
*generated user interface*
A user interface is generated on-the-fly from the schema definition:
entities can be created, displayed, updated and deleted. As display
views are not very fancy, it is usually necessary to develop your
own. Any generated view can be overridden by defining a new one with
the same identifier.
*rql*
Relation Query Language in order to empasize the way of browsing relations.
This query language is inspired by SQL but is highest level, its implementation
generates SQL.
.. _`Python Remote Object`: http://pyro.sourceforge.net/
.. _`yams`: http://www.logilab.org/project/yams/
`CubicWeb` engine
~~~~~~~~~~~~~~~~~
The engine in `CubicWeb` is a set of classes managing a set of objects loaded
dynamically at the startup of `CubicWeb` (*appobjects*). Those dynamics objects,
based on the schema or the library, are building the final application.
The differents dymanic components are for example:
* client and server side
- entities definition, containing the logic which enables application data manipulation
* client side
- *views*, or more specifically
- boxes
- header and footer
- forms
- page templates
- *actions*
- *controllers*
* server side
- notification hooks
- notification views
The components of the engine are:
* a frontal web (only twisted is available so far), transparent for dynamic objects
* an object that encapsulates the configuration
* a `registry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically
Every *appobject* may access to the instance configuration using its *config* attribute
and to the registry using its *vreg* attribute.
API Python/RQL
~~~~~~~~~~~~~~
The Python API developped to interface with RQL is inspired from the standard db-api,
with a Connection object having the methods cursor, rollback and commit essentially.
The most important method is the `execute` method of a cursor :
`execute(rqlstring, args=None, eid_key=None, build_descr=True)`
:rqlstring: the RQL query to execute (unicode)
:args: if the query contains substitutions, a dictionnary containing the values to use
:eid_key:
an implementation detail of the RQL queries cache implies that if a substitution
is used to introduce an eid *susceptible to raise the ambiguities in the query
type resolution*, then we have to specify the correponding key in the dictionnary
through this argument
The `Connection` object owns the methods `commit` and `rollback`. You *should
never need to use them* during the development of the web interface based on
the `CubicWeb` framework as it determines the end of the transaction depending
on the query execution success.
.. note::
While executing updates queries (SET, INSERT, DELETE), if a query generates
an error related to security, a rollback is automatically done on the current
transaction.
The `Request` class (`cubicweb.web`)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A request instance is created when an HTTP request is sent to the web server.
It contains informations such as forms parameters, user authenticated, etc.
**Globally, a request represents a user query, either through HTTP or not
(we also talk about RQL queries on the server side for example).**
An instance of `Request` has the following attributes:
* `user`, instance of `cubicweb.common.utils.User` corresponding to the authenticated
user
* `form`, dictionnary containing the values of a web form
* `encoding`, characters encoding to use in the response
But also:
:Session data handling:
* `session_data()`, returns a dictionnary containing all the session data
* `get_session_data(key, default=None)`, returns a value associated to the given
key or the value `default` if the key is not defined
* `set_session_data(key, value)`, assign a value to a key
* `del_session_data(key)`, suppress the value associated to a key
:Cookies handling:
* `get_cookie()`, returns a dictionnary containing the value of the header
HTTP 'Cookie'
* `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`,
with a minimal 5 minutes length of duration by default (`maxage` = None
returns a *session* cookie which will expire when the user closes the browser
window)
* `remove_cookie(cookie, key)`, forces a value to expire
:URL handling:
* `url()`, returns the full URL of the HTTP request
* `base_url()`, returns the root URL of the web application
* `relative_path()`, returns the relative path of the request
:And more...:
* `set_content_type(content_type, filename=None)`, adds the header HTTP
'Content-Type'
* `get_header(header)`, returns the value associated to an arbitrary header
of the HTTP request
* `set_header(header, value)`, adds an arbitrary header in the response
* `cursor()` returns a RQL cursor on the session
* `execute(*args, **kwargs)`, shortcut to ``.cursor().execute()``
* `property_value(key)`, properties management (`EProperty`)
* dictionnary `data` to store data to share informations between components
*while a request is executed*
Please note that this class is abstract and that a concrete implementation
will be provided by the *frontend* web used (in particular *twisted* as of
today). For the views or others that are executed on the server side,
most of the interface of `Request` is defined in the session associated
to the client.
The `AppObject` class
~~~~~~~~~~~~~~~~~~~~~
In general:
* we do not inherit directly from this class but from a more specific
class such as `AnyEntity`, `EntityView`, `AnyRsetView`,
`Action`...
* to be recordable, a subclass has to define its own register (attribute
`__registry__`) and its identifier (attribute `id`). Usually we do not have
to take care of the register, only the identifier `id`.
We can find a certain number of attributes and methods defined in this class
and common to all the application objects.
At the recording, the following attributes are dynamically added to
the *subclasses*:
* `vreg`, the `vregistry` of the application
* `schema`, the application schema
* `config`, the application configuration
We also find on instances, the following attributes:
* `req`, `Request` instance
* `rset`, the *result set* associated to the object if necessary
* `cursor`, rql cursor on the session
:URL handling:
* `build_url(method=None, **kwargs)`, returns an absolute URL based on
the given arguments. The *controller* supposed to handle the response,
can be specified through the special parameter `method` (the connection
is theoretically done automatically :).
* `datadir_url()`, returns the directory of the application data
(contains static files such as images, css, js...)
* `base_url()`, shortcut to `req.base_url()`
* `url_quote(value)`, version *unicode safe* of the function `urllib.quote`
:Data manipulation:
* `etype_rset(etype, size=1)`, shortcut to `vreg.etype_rset()`
* `eid_rset(eid, rql=None, descr=True)`, returns a *result set* object for
the given eid
* `entity(row, col=0)`, returns the entity corresponding to the data position
in the *result set* associated to the object
* `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but
also call the method `complete()` on the entity before returning it
:Data formatting:
* `format_date(date, date_format=None, time=False)` returns a string for a
mx date time according to application's configuration
* `format_time(time)` returns a string for a mx date time according to
application's configuration
:And more...:
* `external_resource(rid, default=_MARKER)`, access to a value defined in the
configuration file `external_resource`
* `tal_render(template, variables)`, renders a precompiled page template with
variables in the given dictionary as context
.. note::
When we inherit from `AppObject` (even not directly), you *always* have to use
**super()** to get the methods and attributes of the superclasses, and not
use the class identifier.
For example, instead of writting: ::
class Truc(PrimaryView):
def f(self, arg1):
PrimaryView.f(self, arg1)
You'd better write: ::
class Truc(PrimaryView):
def f(self, arg1):
super(Truc, self).f(arg1)
.. _cubesConcepts:
Cubes
~~~~~
What is a cube ?
````````````````
A cube is a model grouping one or more entity types and/or views associated
in order to provide a specific feature or even a complete application using
others cubes.
You can decide to write your own set of cubes if you wish to re-use the
entity types you develop. Lots of cubes are available from the `CubicWeb
Forge`_ under a free software license.
.. _`CubicWeb Forge`: http://www.cubicweb.org/project/
.. _foundationsCube:
Standard structure for a cube
`````````````````````````````
A cube is structured as follows:
::
mycube/
|
|-- data/
| |-- cubes.mycube.css
| |-- cubes.mycube.js
| `-- external_resources
|
|-- debian/
| |-- changelog
| |-- compat
| |-- control
| |-- copyright
| |-- cubicweb-mycube.prerm
| `-- rules
|
|-- entities.py
|
|-- i18n/
| |-- en.po
| `-- fr.po
|
|-- __init__.py
|
|-- MANIFEST.in
|
|-- migration/
| |-- postcreate.py
| `-- precreate.py
|
|-- __pkginfo__.py
|
|-- schema.py
|
|-- setup.py
|
|-- site_cubicweb.py
|
|-- hooks.py
|
|-- test/
| |-- data/
| | `-- bootstrap_cubes
| |-- pytestconf.py
| |-- realdb_test_mycube.py
| `-- test_mycube.py
|
`-- views.py
We can use subpackages instead of python modules for ``views.py``, ``entities.py``,
``schema.py`` or ``hooks.py``. For example, we could have:
::
mycube/
|
|-- entities.py
|-- hooks.py
`-- views/
|-- forms.py
|-- primary.py
`-- widgets.py
where :
* ``schema`` contains the schema definition (server side only)
* ``entities`` contains the entities definition (server side and web interface)
* ``sobjects`` contains hooks and/or views notifications (server side only)
* ``views`` contains the web interface components (web interface only)
* ``test`` contains tests related to the application (not installed)
* ``i18n`` contains messages catalogs for supported languages (server side and
web interface)
* ``data`` contains data files for static content (images, css, javascripts)
...(web interface only)
* ``migration`` contains initialization file for new instances (``postcreate.py``)
and a file containing dependencies of the component depending on the version
(``depends.map``)
* ``debian`` contains all the files managing debian packaging (you will find
the usual files ``control``, ``rules``, ``changelog``... not installed)
* file ``__pkginfo__.py`` provides component meta-data, especially the distribution
and the current version (server side and web interface) or sub-cubes used by
the cube.
At least you should have:
* the file ``__pkginfo__.py``
* the schema definition
XXX false, we may want to have cubes which are only adding a service,
no persistent data (eg embeding for instance)
Standard library
````````````````
A library of standard cubes are available from `CubicWeb Forge`_
Cubes provide entities and views.
The available application entities are:
* addressbook: PhoneNumber and PostalAddress
* basket: Basket (like a shopping cart)
* blog: Blog (a *very* basic blog)
* classfolder: Folder (to organize things but grouping them in folders)
* classtags: Tag (to tag anything)
* file: File (to allow users to upload and store binary or text files)
* link: Link (to collect links to web resources)
* mailinglist: MailingList (to reference a mailing-list and the URLs
for its archives and its admin interface)
* person: Person (easily mixed with addressbook)
* task: Task (something to be done between start and stop date)
* zone: Zone (to define places within larger places, for example a
city in a state in a country)
The available system entities are:
* comment: Comment (to attach comment threads to entities)