--- a/appobject.py Tue Apr 06 18:51:17 2010 +0200
+++ b/appobject.py Tue Apr 06 19:08:07 2010 +0200
@@ -1,11 +1,18 @@
-"""Base class for dynamically loaded objects accessible through the vregistry.
-
-You'll also find some convenience classes to build selectors.
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+The `AppObject` class
+---------------------
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+The AppObject class is the base class for all dynamically loaded objects
+(application objects) accessible through the vregistry.
+
+We can find a certain number of attributes and methods defined in this class and
+common to all the application objects.
+
+.. autoclass:: AppObject
"""
__docformat__ = "restructuredtext en"
@@ -21,13 +28,17 @@
# selector base classes and operations ########################################
def objectify_selector(selector_func):
- """convenience decorator for simple selectors where a class definition
- would be overkill::
+ """Most of the time, a simple score function is enough to build a selector.
+ The :func:`objectify_selector` decorator turn it into a proper selector
+ class::
@objectify_selector
def one(cls, *args, **kwargs):
return 1
+ class MyView(View):
+ __select__ = View.__select__ & one()
+
"""
return type(selector_func.__name__, (Selector,),
{'__doc__': selector_func.__doc__,
@@ -49,7 +60,7 @@
class Selector(object):
"""base class for selector classes providing implementation
- for operators ``&`` and ``|``
+ for operators ``&``, ``|`` and ``~``
This class is only here to give access to binary operators, the
selector logic itself should be implemented in the __call__ method
@@ -205,42 +216,77 @@
selected according to a context (usually at least a request and a result
set).
- Concrete application objects classes are designed to be loaded by the
- vregistry and should be accessed through it, not by direct instantiation.
+ The following attributes should be set on concret appobject classes:
- The following attributes should be set on concret appobject classes:
- :__registry__:
+ :attr:`__registry__`
name of the registry for this object (string like 'views',
'templates'...)
- :__regid__:
+
+ :attr:`__regid__`
object's identifier in the registry (string like 'main',
'primary', 'folder_box')
- :__select__:
+
+ :attr:`__select__`
class'selector
- Moreover, the `__abstract__` attribute may be set to True to indicate
- that a appobject is abstract and should not be registered.
+ Moreover, the `__abstract__` attribute may be set to True to indicate that a
+ class is abstract and should not be registered.
At selection time, the following attributes are set on the instance:
- :_cw:
+ :attr:`_cw`
current request
- :cw_extra_kwargs:
+ :attr:`cw_extra_kwargs`
other received arguments
- only if rset is found in arguments (in which case rset/row/col will be
- removed from cwextra_kwargs):
+ And also the following, only if `rset` is found in arguments (in which case
+ rset/row/col will be removed from `cwextra_kwargs`):
- :cw_rset:
+ :attr:`cw_rset`
context result set or None
- :cw_row:
+
+ :attr:`cw_row`
if a result set is set and the context is about a particular cell in the
result set, and not the result set as a whole, specify the row number we
are interested in, else None
- :cw_col:
+
+ :attr:`cw_col`
if a result set is set and the context is about a particular cell in the
result set, and not the result set as a whole, specify the col number we
are interested in, else None
+
+
+ .. Note::
+
+ * 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 registry (attribute
+ `__registry__`) and its identifier (attribute `__regid__`). Usually
+ you don't have to take care of the registry since it's set by the base
+ class, only the identifier `id`
+
+ * application objects are designed to be loaded by the vregistry and
+ should be accessed through it, not by direct instantiation, besides
+ to use it as base classe.
+
+
+ * 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 must write::
+
+ class Truc(PrimaryView):
+ def f(self, arg1):
+ super(Truc, self).f(arg1)
+
"""
__registry__ = None
__regid__ = None
--- a/cwconfig.py Tue Apr 06 18:51:17 2010 +0200
+++ b/cwconfig.py Tue Apr 06 19:08:07 2010 +0200
@@ -1,45 +1,42 @@
# -*- coding: utf-8 -*-
-"""common configuration utilities for cubicweb
+#:organization: Logilab
+#:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+#:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+
+# docstring included in doc/book/en/admin/setup.rst
+"""
+.. _ResourceMode:
+
+Resource mode
+-------------
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+A resource *mode* is a predifined set of settings for various resources
+directories, such as cubes, instances, etc. to ease development with the
+framework. There are two running modes with |cubicweb|:
+
+* 'user', resources are searched / created in the user home directory:
+
+ - instances are stored in :file:`~/etc/cubicweb.d`
+ - temporary files (such as pid file) in :file:`/tmp`
+
+* 'system', resources are searched / created in the system directories (eg
+ usually requiring root access):
+
+ - instances are stored in :file:`<INSTALL_PREFIX>/etc/cubicweb.d`
+ - temporary files (such as pid file) in :file:`/var/run/cubicweb`
+
+ where `<INSTALL_PREFIX>` is the detected installation prefix ('/usr/local' for
+ instance).
-If cubicweb is a mercurial checkout (eg `CWDEV` is true), located in
-`CW_SOFTWARE_ROOT`:
-
- * main cubes directory is `<CW_SOFTWARE_ROOT>/../cubes`. You can specify
- another one with `CW_INSTANCES_DIR` environment variable or simply add some
- other directories by using `CW_CUBES_PATH`.
-
- * cubicweb migration files are by default searched in
- `<CW_SOFTWARE_ROOT>/misc/migration` instead of
- `<install prefix>/share/cubicweb/migration/`
-
- * Cubicweb will start in 'user' mode (see below)
-
-
-On startup, Cubicweb is using a specific *mode*. A mode corresponds to some
-default setting for various resource directories. There are currently 2 main
-modes : 'system', for system wide installation, and 'user', fur user local
-installation (e.g. no root privileges).
-
-'user' mode is activated automatically when cubicweb is a mercurial checkout
-(e.g. has a .hg directory). You can also force mode by using the `CW_MODE`
-environment variable, to:
-
-* use system wide installation but user specific instances and all, without root
- privileges on the system (`export CW_MODE=user`)
-
-* use local checkout of cubicweb on system wide instances (requires root
- privileges on the system (`export CW_MODE=system`)
-
- Here is the default resource directories settings according to mode:
+Notice that each resource path may be explicitly set using an environment
+variable if the default doesn't suit your needs. Here are the default resource
+directories that are affected according to mode:
* 'system': ::
- CW_INSTANCES_DIR = /etc/cubicweb.d/
+ CW_INSTANCES_DIR = <INSTALL_PREFIX>/etc/cubicweb.d/
CW_INSTANCES_DATA_DIR = /var/lib/cubicweb/instances/
CW_RUNTIME_DIR = /var/run/cubicweb/
@@ -49,24 +46,79 @@
CW_INSTANCES_DATA_DIR = ~/etc/cubicweb.d/
CW_RUNTIME_DIR = /tmp
+Cubes search path is also affected, see the :ref:Cube section.
+
+By default, the mode automatically set to 'user' if a :file:`.hg` directory is found
+in the cubicweb package, else it's set to 'system'. You can force this by setting
+the :envvar:`CW_MODE` environment variable to either 'user' or 'system' so you can
+easily:
+
+* use system wide installation but user specific instances and all, without root
+ privileges on the system (`export CW_MODE=user`)
+
+* use local checkout of cubicweb on system wide instances (requires root
+ privileges on the system (`export CW_MODE=system`)
+
+If you've a doubt about the mode you're currently running, check the first line
+outputed by the :command:`cubicweb-ctl list` command.
+
+Also, if cubicweb is a mercurial checkout located in `<CW_SOFTWARE_ROOT>`:
+
+* main cubes directory is `<CW_SOFTWARE_ROOT>/../cubes`. You can specify
+ another one with :envvar:`CW_INSTANCES_DIR` environment variable or simply
+ add some other directories by using :envvar:`CW_CUBES_PATH`
+
+* cubicweb migration files are searched in `<CW_SOFTWARE_ROOT>/misc/migration`
+ instead of `<INSTALL_PREFIX>/share/cubicweb/migration/`.
+
+
+.. _ConfigurationEnv:
+
+Environment configuration
+-------------------------
+
+Python
+``````
+
+If you installed |cubicweb| by cloning the Mercurial forest or from source
+distribution, then you will need to update the environment variable PYTHONPATH by
+adding the path to the forest `cubicweb`:
+
+Add the following lines to either :file:`.bashrc` or :file:`.bash_profile` to
+configure your development environment ::
+
+ export PYTHONPATH=/full/path/to/cubicweb-forest
+
+If you installed |cubicweb| with packages, no configuration is required and your
+new cubes will be placed in `/usr/share/cubicweb/cubes` and your instances will
+be placed in `/etc/cubicweb.d`.
+
+
+CubicWeb
+````````
+
+Here are all environment variables that may be used to configure |cubicweb|:
.. envvar:: CW_MODE
- Resource mode: user or system
+
+ Resource mode: user or system, as explained in :ref:ResourceMode.
.. envvar:: CW_CUBES_PATH
- Augments the default search path for cubes
+
+ Augments the default search path for cubes. You may specify several
+ directories using ':' as separator (';' under windows environment).
.. envvar:: CW_INSTANCES_DIR
- Directory where cubicweb instances will be found
+
+ Directory where cubicweb instances will be found.
.. envvar:: CW_INSTANCES_DATA_DIR
- Directory where cubicweb instances data will be written
+
+ Directory where cubicweb instances data will be written (backup file...)
.. envvar:: CW_RUNTIME_DIR
+
Directory where pid files will be written
-
-
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"
_ = unicode
--- a/cwvreg.py Tue Apr 06 18:51:17 2010 +0200
+++ b/cwvreg.py Tue Apr 06 19:08:07 2010 +0200
@@ -1,9 +1,178 @@
-"""extend the generic VRegistry with some cubicweb specific stuff
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+""".. VRegistry:
+
+The `VRegistry`
+---------------
+
+The `VRegistry` can be seen as a two level dictionary. It contains all objects
+loaded dynamically to build a |cubicweb| application. Basically:
+
+* first level key return a *registry*. This key corresponds to the `__registry__`
+ attribute of application object classes
+
+* second level key return a list of application objects which share the same
+ identifier. This key corresponds to the `__regid__` attribute of application
+ object classes.
+
+A *registry* hold a specific kind of application objects. You've for instance
+a registry for entity classes, another for views, etc...
+
+The `VRegistry` has two main responsibilities:
+
+- being the access point to all registries
+
+- handling the registration process at startup time, and during automatic
+ reloading in debug mode.
+
+
+.. _AppObjectRecording:
+
+Details of the recording process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. index::
+ vregistry: registration_callback
+
+On startup, |cubicweb| have to load application objects defined in its library
+and in cubes used by the instance. Application objects from the library are
+loaded first, then those provided by cubes are loaded in an ordered way (e.g. if
+your cube depends on an other, objects from the dependancy will be loaded
+first). Cube's modules or packages where appobject are looked at is explained in
+:ref:`cubelayout`.
+
+For each module:
+
+* by default all objects are registered automatically
+
+* if some objects have to replace other objects, or be included only if some
+ condition is true, you'll have to define a `registration_callback(vreg)`
+ function in your module and explicitly register **all objects** in this module,
+ using the api defined below.
+
+.. Note::
+ Once the function `registration_callback(vreg)` is implemented in a module,
+ all the objects from this module have to be explicitly registered as it
+ disables the automatic objects registration.
+
+
+API for objects registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here are the registration methods that you can use in the `registration_callback`
+to register your objects to the `VRegistry` instance given as argument (usually
+named `vreg`):
+
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
+
+
+Examples:
+
+.. sourcecode:: python
+
+ # web/views/basecomponents.py
+ def registration_callback(vreg):
+ # register everything in the module except SeeAlsoComponent
+ vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
+ # conditionally register SeeAlsoVComponent
+ if 'see_also' in vreg.schema:
+ vreg.register(SeeAlsoVComponent)
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+In this example, we register all application object classes defined in the module
+except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
+relation type is defined in the instance'schema.
+
+.. sourcecode:: python
+
+ # goa/appobjects/sessions.py
+ def registration_callback(vreg):
+ vreg.register(SessionsCleaner)
+ # replace AuthenticationManager by GAEAuthenticationManager
+ vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
+ # replace PersistentSessionManager by GAEPersistentSessionManager
+ vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
+
+In this example, we explicitly register classes one by one:
+
+* the `SessionCleaner` class
+* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
+* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
+
+If at some point we register a new appobject class in this module, it won't be
+registered at all without modification to the `registration_callback`
+implementation. The previous example will register it though, thanks to the call
+to the `register_all` method.
+
+
+.. _Selection:
+
+Runtime objects selection
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we've all application objects loaded, the question is : when I want some
+specific object, for instance the primary view for a given entity, how do I get
+the proper object ? This is what we call the **selection mechanism**.
+
+As explained in the :ref:`Concepts` section:
+
+* each application object has a **selector**, defined by its `__select__` class attribute
+
+* this selector is responsible to return a **score** for a given context
+
+ - 0 score means the object doesn't apply to this context
+
+ - else, the higher the score, the better the object suits the context
+
+* the object with the higher score is selected.
+
+.. Note::
+
+ When no score is higher than the others, an exception is raised in development
+ mode to let you know that the engine was not able to identify the view to
+ apply. This error is silenced in production mode and one of the objects with
+ the higher score is picked.
+
+ In such cases you would need to review your design and make sure your selectors
+ or appobjects are properly defined.
+
+For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
+`__registry__ = 'views'`) for a result set containing a `Card` entity, 2 objects
+will probably be selectable:
+
+* the default primary view (`__select__ = implements('Any')`), meaning
+ that the object is selectable for any kind of entity type
+
+* the specific `Card` primary view (`__select__ = implements('Card')`,
+ meaning that the object is selectable for Card entities
+
+Other primary views specific to other entity types won't be selectable in this
+case. Among selectable objects, the implements selector will return a higher
+score than the second view since it's more specific, so it will be selected as
+expected.
+
+.. _SelectionAPI:
+
+API for objects selections
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is the selection API you'll get on every registry. Some of them, as the
+'etypes' registry, containing entity classes, extend it. In those methods,
+`*args, **kwargs` is what we call the **context**. Those arguments are given to
+selectors that will inspect there content and return a score accordingly.
+
+.. automethod:: cubicweb.vregistry.Registry.select
+
+.. automethod:: cubicweb.vregistry.Registry.select_or_none
+
+.. automethod:: cubicweb.vregistry.Registry.possible_objects
+
+.. automethod:: cubicweb.vregistry.Registry.object_by_id
"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -347,8 +516,11 @@
obj.schema = schema
def register_if_interface_found(self, obj, ifaces, **kwargs):
- """register an object but remove it if no entity class implements one of
- the given interfaces at the end of the registration process
+ """register `obj` but remove it if no entity class implements one of
+ the given `ifaces` interfaces at the end of the registration process.
+
+ Extra keyword arguments are given to the
+ :meth:`~cubicweb.cwvreg.CubicWebVRegistry.register` function.
"""
self.register(obj, **kwargs)
if not isinstance(ifaces, (tuple, list)):
@@ -357,6 +529,13 @@
self._needs_iface[obj] = ifaces
def register(self, obj, *args, **kwargs):
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified, with identifier `oid` or
+ `obj.__regid__` if not specified.
+
+ If `clear` is true, all objects with the same identifier will be
+ previously unregistered.
+ """
super(CubicWebVRegistry, self).register(obj, *args, **kwargs)
# XXX bw compat
ifaces = use_interfaces(obj)
--- a/devtools/testlib.py Tue Apr 06 18:51:17 2010 +0200
+++ b/devtools/testlib.py Tue Apr 06 19:08:07 2010 +0200
@@ -363,9 +363,11 @@
self.vreg._loadedmods.setdefault(self.__module__, {})
for obj in appobjects:
self.vreg.register(obj)
- yield
- for obj in appobjects:
- self.vreg.unregister(obj)
+ try:
+ yield
+ finally:
+ for obj in appobjects:
+ self.vreg.unregister(obj)
# vregistry inspection utilities ###########################################
--- a/doc/book/en/admin/setup.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/admin/setup.rst Tue Apr 06 19:08:07 2010 +0200
@@ -8,16 +8,17 @@
Installation of `Cubicweb` and its dependencies
-----------------------------------------------
-*CubicWeb* is packaged for Debian and Ubuntu, but can be installed from source
+|cubicweb| is packaged for Debian and Ubuntu, but can be installed from source
using a tarball or the Mercurial version control system.
+
.. _DebianInstallation:
Debian and Ubuntu packages
```````````````````````````
-Depending on the distribution you are using, add the appropriate line to your list
-of sources (for example by editing ``/etc/apt/sources.list``).
+Depending on the distribution you are using, add the appropriate line to your
+list of sources (for example by editing ``/etc/apt/sources.list``).
For Debian Lenny::
@@ -37,21 +38,26 @@
apt-get update
apt-get install cubicweb cubicweb-dev
-`cubicweb` installs the framework itself, allowing you to create
-new instances.
+
+`cubicweb` installs the framework itself, allowing you to create new instances.
+
+`cubicweb-dev` installs the development environment allowing you to develop new
+cubes.
-`cubicweb-dev` installs the development environment allowing you to
-develop new cubes.
+There is also a wide variety of cubes listed on the `CubicWeb.org Forge`_
+available as debian packages and tarball.
-There is also a wide variety of cubes listed on http://www.cubicweb.org/Project available as debian packages and tarball.
+The repositories are signed with `Logilab's gnupg key`_. To avoid warning on
+"apt-get update":
-The repositories are signed with `Logilab's gnupg key`_. To avoid warning on "apt-get update":
1. become root using sudo
2. download http://ftp.logilab.org/dists/logilab-dists-key.asc using e.g. wget
3. run "apt-key add logilab-dists-key.asc"
4. re-run apt-get update (manually or through the package manager, whichever you prefer)
.. _`Logilab's gnupg key`: http://ftp.logilab.org/dists/logilab-dists-key.asc
+.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
+
.. _SourceInstallation:
@@ -66,6 +72,11 @@
Make sure you have installed the dependencies (see appendixes for the list).
+|cubicweb| should soon be pip_ installable, stay tuned (expected in 3.8).
+
+.. _pip: http://pypi.python.org/pypi/pip
+
+
Install from version control system
```````````````````````````````````
@@ -85,32 +96,31 @@
Make sure you have installed the dependencies (see appendixes for the list).
+
.. _WindowsInstallation:
Windows installation
````````````````````
Base elements
-_____________
+~~~~~~~~~~~~~
-Setting up a windows development environment is not too complicated
-but requires a series of small steps. What is proposed there is only
-an example of what can be done. We assume everything goes into C:\ in
-this document. Adjusting the installation drive should be
-straightforward.
+Setting up a windows development environment is not too complicated but requires
+a series of small steps. What is proposed there is only an example of what can be
+done. We assume everything goes into `C:\\` in this document. Adjusting the
+installation drive should be straightforward.
-You should start by downloading and installing the Python(x,y)
-distribution. It contains python 2.5 plus numerous useful third-party
-modules and applications::
+You should start by downloading and installing the Python(x,y) distribution. It
+contains python 2.5 plus numerous useful third-party modules and applications::
http://www.pythonxy.com/download_fr.php
-At the time of this writting, one gets version 2.1.15. Among the many
-things provided, one finds Eclipse + pydev (an arguably good IDE for
-python under windows).
+At the time of this writting, one gets version 2.1.15. Among the many things
+provided, one finds Eclipse + pydev (an arguably good IDE for python under
+windows).
-Then you must grab Twisted. There is a windows installer directly
-available from this page::
+Then you must grab Twisted. There is a windows installer directly available from
+this page::
http://twistedmatrix.com/trac/
@@ -129,11 +139,9 @@
http://www.stickpeople.com/projects/python/win-psycopg/#Version2
-Please be careful to select the right python (2.5) and postgres (8.4)
-versions.
+Please be careful to select the right python (2.5) and postgres (8.4) versions.
-Pyro enables remote access to cubicweb repository instances. Get it
-there::
+Pyro enables remote access to cubicweb repository instances. Get it there::
http://sourceforge.net/projects/pyro/files/
@@ -144,26 +152,26 @@
Check out the latest release.
-Having graphviz will allow schema drawings, which is quite recommended
-(albeit not mandatory). You should get an msi installer there::
+Having graphviz will allow schema drawings, which is quite recommended (albeit
+not mandatory). You should get an msi installer there::
http://www.graphviz.org/Download_windows.php
-Simplejson will be provided within the forest, but a win32 compiled
-version will run much faster::
+Simplejson will be provided within the forest, but a win32 compiled version will
+run much faster::
http://www.osuch.org/python-simplejson%3Awin32
Tools
-_____
+~~~~~
-Get mercurial + its standard windows GUI (TortoiseHG) there (the
-latest is the greatest)::
+Get mercurial + its standard windows GUI (TortoiseHG) there (the latest is the
+greatest)::
http://bitbucket.org/tortoisehg/stable/wiki/download
-If you need to peruse mercurial over ssh, it can be helpful to get an
-ssh client like Putty::
+If you need to peruse mercurial over ssh, it can be helpful to get an ssh client
+like Putty::
http://www.putty.org/
@@ -173,10 +181,9 @@
http://www.vectrace.com/mercurialeclipse/
Setting up the sources
-______________________
+~~~~~~~~~~~~~~~~~~~~~~
-You need to enable the mercurial forest extension. To do this, edit
-the file::
+You need to enable the mercurial forest extension. To do this, edit the file::
C:\Program Files\TortoiseHg\Mercurial.ini
@@ -185,8 +192,8 @@
forest=C:\Program Files\TortoiseHg\ext\forest\forest.py
Now, you need to clone the cubicweb repository. We assume that you use
-Eclipse. From the IDE, choose File -> Import. In the box, select
-`Mercurial/Clone repository using MercurialEclipse`.
+Eclipse. From the IDE, choose File -> Import. In the box, select `Mercurial/Clone
+repository using MercurialEclipse`.
In the import main panel you just have to:
@@ -194,28 +201,26 @@
* check the 'Repository is a forest' box.
-Then, click on 'Finish'. It might take some time to get it all. Note
-that the `cubicwin32` forest contains additional python packages such
-as yapps, vobject, simplejson and twisted-web2 which are not provided
-with Python(x,y). This is provided for convenience, as we do not
-ensure the up-to-dateness of these packages, especially with respect
-to security fixes.
+Then, click on 'Finish'. It might take some time to get it all. Note that the
+`cubicwin32` forest contains additional python packages such as yapps, vobject,
+simplejson and twisted-web2 which are not provided with Python(x,y). This is
+provided for convenience, as we do not ensure the up-to-dateness of these
+packages, especially with respect to security fixes.
Environment variables
-_____________________
+~~~~~~~~~~~~~~~~~~~~~
-You will need some convenience environment variables once all is set
-up. These variables are settable through the GUI by getting at the
-'System properties' window (by righ-clicking on 'My Computer' ->
-properties).
+You will need some convenience environment variables once all is set up. These
+variables are settable through the GUI by getting at the 'System properties'
+window (by righ-clicking on 'My Computer' -> properties).
-In the 'advanced' tab, there is an 'Environment variables'
-button. Click on it. That opens a small window allowing edition of
-user-related and system-wide variables.
+In the 'advanced' tab, there is an 'Environment variables' button. Click on
+it. That opens a small window allowing edition of user-related and system-wide
+variables.
-We will consider only user variables. First, the PATH variable. You
-should ensure it contains, separated by semi-colons, and assuming you
-are logged in as user Jane::
+We will consider only user variables. First, the PATH variable. You should ensure
+it contains, separated by semi-colons, and assuming you are logged in as user
+Jane::
C:\Documents and Settings\Jane\My Documents\Python\cubicweb\cubicweb\bin
C:\Program Files\Graphviz2.24\bin
@@ -231,13 +236,13 @@
... and get a meaningful output.
Running an instance as a service
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This currently assumes that the instances configurations is located
-at C:\\etc\\cubicweb.d.
+This currently assumes that the instances configurations is located at
+C:\\etc\\cubicweb.d.
-For a cube 'my_cube', you will then find C:\\etc\\cubicweb.d\\my_cube\\win32svc.py
-that has to be used thusly::
+For a cube 'my_cube', you will then find
+C:\\etc\\cubicweb.d\\my_cube\\win32svc.py that has to be used thusly::
win32svc install
@@ -248,17 +253,6 @@
should start the service.
-PostgreSQL installation
-```````````````````````
-
-Please refer to the `PostgreSQL project online documentation`_.
-
-.. _`PostgreSQL project online documentation`: http://www.postgresql.org/
-
-You need to install the three following packages: `postgresql-8.3`,
-`postgresql-contrib-8.3` and `postgresql-plpython-8.3`.
-
-
Other dependencies
``````````````````
@@ -271,103 +265,115 @@
* `python-ldap` if you plan to use a LDAP source on the server
-.. _ConfigurationEnv:
-Environment configuration
--------------------------
-
-If you installed *CubicWeb* by cloning the Mercurial forest, then you
-will need to update the environment variable PYTHONPATH by adding
-the path to the forest ``cubicweb``:
-
-Add the following lines to either `.bashrc` or `.bash_profile` to configure
-your development environment ::
-
- export PYTHONPATH=/full/path/to/cubicweb-forest
-
-If you installed *CubicWeb* with packages, no configuration is required and your
-new cubes will be placed in `/usr/share/cubicweb/cubes` and your instances
-will be placed in `/etc/cubicweb.d`.
-
-You may run a system-wide install of *CubicWeb* in "user mode" and use it for
-development by setting the following environment variable::
-
- export CW_MODE=user
- export CW_CUBES_PATH=~/lib/cubes
- export CW_INSTANCES_DIR=~/etc/cubicweb.d/
- export CW_INSTANCES_DATA_DIR=$CW_INSTANCES_DIR
- export CW_RUNTIME_DIR=/tmp
-
-.. note::
- The values given above are our suggestions but of course
- can be different.
-
+.. _DatabaseInstallation:
Databases configuration
-----------------------
-.. _ConfigurationPostgresql:
+Whatever the backend used, database connection information are stored in the
+instance's :file:`sources` file. Currently cubicweb has been tested using
+Postgresql (recommanded), MySQL, SQLServer and SQLite.
+
+.. _PostgresqlConfiguration:
PostgreSQL configuration
````````````````````````
-.. note::
- If you already have an existing cluster and PostgreSQL server
- running, you do not need to execute the initilization step
- of your PostgreSQL database.
+For installation, please refer to the `PostgreSQL project online documentation`_.
+
+.. _`PostgreSQL project online documentation`: http://www.postgresql.org/
+
+You need to install the three following packages: `postgresql-8.X`,
+`postgresql-client-8.X`, and `postgresql-plpython-8.X`. If you run postgres
+version prior to 8.3, you'll also need the `postgresql-contrib-8.X` package for
+full-text search extension.
-* First, initialize the database PostgreSQL with the command ``initdb``.
+If you run postgres on another host than the |cubicweb| repository, you should
+install the `postgresql-client` package on the |cubicweb| host, and others on the
+database host.
+
+.. Note::
+
+ If you already have an existing cluster and PostgreSQL server running, you do
+ not need to execute the initilization step of your PostgreSQL database unless
+ you want a specific cluster for |cubicweb| databases or if your existing
+ cluster doesn't use the UTF8 encoding (see note below).
+
+* First, initialize a PostgreSQL cluster with the command ``initdb``.
::
- $ initdb -D /path/to/pgsql
+ $ initdb -E UTF8 -D /path/to/pgsql
+
+ Notice the encoding specification. This is necessary since |cubicweb| usually
+ want UTF8 encoded database. If you use a cluster with the wrong encoding, you'll
+ get error like::
- Once initialized, start the database server PostgreSQL
- with the command::
+ new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII)
+ HINT: Use the same encoding as in the template database, or use template0 as template.
+
+
+ Once initialized, start the database server PostgreSQL with the command::
$ postgres -D /path/to/psql
- If you cannot execute this command due to permission issues, please
- make sure that your username has write access on the database.
- ::
+ If you cannot execute this command due to permission issues, please make sure
+ that your username has write access on the database. ::
$ chown username /path/to/pgsql
-* The database authentication can be either set to `ident sameuser`
- or `md5`.
- If set to `md5`, make sure to use an existing user
- of your database.
- If set to `ident sameuser`, make sure that your
- client's operating system user name has a matching user in
- the database. If not, please do as follow to create a user::
+* The database authentication can be either set to `ident sameuser` or `md5`. If
+ set to `md5`, make sure to use an existing user of your database. If set to
+ `ident sameuser`, make sure that your client's operating system user name has a
+ matching user in the database. If not, please do as follow to create a user::
$ su
$ su - postgres
$ createuser -s -P username
- The option `-P` (for password prompt), will encrypt the password with
- the method set in the configuration file ``pg_hba.conf``.
- If you do not use this option `-P`, then the default value will be null
- and you will need to set it with::
+ The option `-P` (for password prompt), will encrypt the password with the
+ method set in the configuration file :file:`pg_hba.conf`. If you do not use this
+ option `-P`, then the default value will be null and you will need to set it
+ with::
$ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql"
- This login/password will be requested when you will create an
- instance with `cubicweb-ctl create` to initialize the database of
- your instance.
-
-.. note::
- The authentication method can be configured in ``pg_hba.conf``.
+.. Note::
+ The authentication method can be configured in file:`pg_hba.conf`.
-.. FIXME Are these steps really necessary? It seemed to work without.
+The above login/password will be requested when you will create an instance with
+`cubicweb-ctl create` to initialize the database of your instance.
-* Installation of plain-text index extension ::
+Notice that the `cubicweb-ctl db-create` does database initialization that
+may requires a postgres superuser. That's why a login/password is explicitly asked
+at this step, so you can use there a superuser without using this user when running
+the instance. Things that require special privileges at this step:
+
+* database creation, require the 'create database' permission
+* install the plpython extension language (require superuser)
+* install the tsearch extension for postgres version prior to 8.3 (require superuser)
- cat /usr/share/postgresql/8.3/contrib/tsearch2.sql | psql -U username template1
+To avoid using a super user each time you create an install, a nice trick is to
+install plpython (and tsearch when needed) on the special `template1` database,
+so they will be installed automatically when cubicweb databases are created
+without even with needs for special access rights. To do so, run ::
+
+ # Installation of plpythonu language by default ::
+ $ createlang -U pgadmin plpythonu template1
+ $ psql -U pgadmin template1
+ template1=# update pg_language set lanpltrusted=TRUE where lanname='plpythonu';
-* Installation of plpythonu language by default ::
+Where `pgadmin` is a postgres superuser. The last command is necessary since by
+default plpython is an 'untrusted' language and as such can't be used by non
+superuser. This update fix that problem by making it trusted.
- createlang -U pgadmin plpythonu template1
+To install the tsearch plain-text index extension on postgres prior to 8.3, run::
+
+ cat /usr/share/postgresql/8.X/contrib/tsearch2.sql | psql -U username template1
+
+
+.. _MySqlConfiguration:
MySql configuration
```````````````````
@@ -378,19 +384,22 @@
default-character-set=utf8
max_allowed_packet = 128M
-.. note::
+.. Note::
It is unclear whether mysql supports indexed string of arbitrary lenght or
not.
+
+.. _SQLServerConfiguration:
+
SQLServer configuration
------------------------
+```````````````````````
-As of this writing, sqlserver support is in progress. You should be
-able to connect, create a database and go quite far, but some of the
-generated SQL is still currently not accepted by the backend.
+As of this writing, sqlserver support is in progress. You should be able to
+connect, create a database and go quite far, but some of the generated SQL is
+still currently not accepted by the backend.
-The `source` configuration file may look like this (specific parts
-only are shown)::
+The `source` configuration file may look like this (specific parts only are
+shown)::
[system]
db-driver=sqlserver2005
@@ -402,17 +411,43 @@
db-encoding=utf8
+
+.. _SQLiteConfiguration:
+
+SQLite configuration
+````````````````````
+SQLite has the great advantage of requiring almost no configuration. Simply
+use 'sqlite' as db-driver, and set path to the dabase as db-name. Don't specify
+anything for db-user and db-password, they will be ignore anyway.
+
+.. Note::
+ SQLite is great for testing and to play with cubicweb but is not suited for
+ production environments.
+
+
+.. _PyroConfiguration:
+
Pyro configuration
------------------
-If you use Pyro, it is required to have a name server Pyro running on your
-network (by default it is detected by a broadcast request).
+If you want to use Pyro to access your instance remotly, or to have multi-source
+or distributed configuration, it is required to have a name server Pyro running
+on your network. By by default it is detected by a broadcast request, but you can
+specify a location in the instance's configuration file.
To do so, you need to :
-* launch the server manually before starting cubicweb as a server with
- `pyro-nsd start`
+* launch the server manually before starting cubicweb as a server with `pyro-nsd
+ start`
+
+* under debian, edit the file :file:`/etc/default/pyro-nsd` so that the name
+ server pyro will be launched automatically when the machine fire up
+
-* edit the file ``/etc/default/pyro-nsd`` so that the name server pyro
- will be launched automatically when the machine fire up
+Cubicweb resources configuration
+--------------------------------
+.. autodocstring:: cubicweb.cwconfig
+
+
+.. |cubicweb| replace:: *CubicWeb*
--- a/doc/book/en/conf.py Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/conf.py Tue Apr 06 19:08:07 2010 +0200
@@ -19,6 +19,7 @@
# serve to show the default value.
import sys, os
+
from cubicweb import __pkginfo__ as cw
# If your extensions are in another directory, add it here. If the directory
@@ -31,7 +32,7 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc']
+extensions = ['sphinx.ext.autodoc', 'logilab.common.sphinx_ext']
autoclass_content = 'both'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']
--- a/doc/book/en/development/datamodel/definition.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/datamodel/definition.rst Tue Apr 06 19:08:07 2010 +0200
@@ -11,21 +11,20 @@
At this point, it is important to make clear the difference between
*relation type* and *relation definition*: a *relation type* is only a relation
-name with potentially other additionnal properties (see below), whereas a
+name with potentially other additional properties (see below), whereas a
*relation definition* is a complete triplet
"<subject entity type> <relation type> <object entity type>".
-A relation type could have been implied if none is related to a
-relation definition of the schema.
-Also, it should be clear that to properly handle data migration, an instance'schema
-is stored in the database, so the python schema file used to defined it are only readen
+Also, it should be clear that to properly handle data migration, an
+instance's schema
+is stored in the database, so the python schema file used to defined it is only read
when the instance is created or upgraded.
-The following built-in types are available : `String`, `Int`, `Float`,
+The following built-in types are available: `String`, `Int`, `Float`,
`Decimal`, `Boolean`, `Date`, `Datetime`, `Time`, `Interval`, `Byte`
and `Password`.
-You'll also have access to :ref:`base cubicweb entity types <CWBaseEntityTypes>`.
+You'll also have access to :ref:`base CubicWeb entity types <CWBaseEntityTypes>`.
The instance schema is accessible through the .schema attribute of the
`vregistry`. It's an instance of :class:`cubicweb.schema.Schema`, which
@@ -33,117 +32,127 @@
:note:
In previous yams versions, almost all classes where available without
- any import, but the should now be explicitely imported.
+ any import, but the should now be explicitly imported.
Entity type
~~~~~~~~~~~
-It's an instance of :class:`yams.schema.EntitySchema`. Each entity types has
-a set of attributes and relation and some permissions, defining who can add, read,
+An entity type is an instance of :class:`yams.schema.EntitySchema`. Each entity type has
+a set of attributes and relations, and some permissions which define who can add, read,
update or delete entities of this type.
XXX yams inheritance
Relation type
~~~~~~~~~~~~~
-It's an instance of :class:`yams.schema.RelationSchema`. A relation type is simply
-a semantic definition of a kind of relationship that may occurs in your application.
+A relation type is an instance of :class:`yams.schema.RelationSchema`. A relation type is simply
+a semantic definition of a kind of relationship that may occur in an application.
-It's important to choose a good name, at least to avoid conflicts with some semantically
-different relation defined in other cubes (since we've no namespace yet).
+It is important to choose a good name, at least to avoid conflicts with some semantically
+different relation defined in other cubes (since we've no name space yet).
-A relation type hold the following properties (which are hence shared between all
+A relation type holds the following properties (which are hence shared between all
relation definitions of that type):
-* `inlined` : boolean handling the physical optimization for archiving
+* `inlined`: boolean handling the physical optimization for archiving
the relation in the subject entity table, instead of creating a specific
table for the relation. This applies to relations where cardinality
of subject->relation->object is 0..1 (`?`) or 1..1 (`1`) for *all* its relation
definitions.
-* `symmetric` : boolean indicating that the relation is symmetrical, which
+* `symmetric`: boolean indicating that the relation is symmetrical, which
means that `X relation Y` implies `Y relation X`.
Relation definition
~~~~~~~~~~~~~~~~~~~
-It's an instance of :class:`yams.schema.RelationDefinition`. It is a complete triplet
+A relation definition is an instance of :class:`yams.schema.RelationDefinition`. It is a complete triplet
"<subject entity type> <relation type> <object entity type>".
+When creating a new instance of that class, the corresponding
+:class:`RelationType` instance is created on the fly if necessary.
+
+
Properties
``````````
-* Optional properties for attributes and relations :
+* Optional properties for attributes and relations:
- - `description` : a string describing an attribute or a relation. By default
+ - `description`: a string describing an attribute or a relation. By default
this string will be used in the editing form of the entity, which means
that it is supposed to help the end-user and should be flagged by the
function `_` to be properly internationalized.
- - `constraints` : a list of conditions/constraints that the relation has to
+ - `constraints`: a list of conditions/constraints that the relation has to
satisfy (c.f. `Constraints`_)
- - `cardinality` : a two character string which specify the cardinality of the
+ - `cardinality`: a two character string specifying the cardinality of the
relation. The first character defines the cardinality of the relation on
the subject, and the second on the object. When a relation can have
multiple subjects or objects, the cardinality applies to all,
not on a one-to-one basis (so it must be consistent...). The possible
- values are inspired from regular expression syntax :
+ values are inspired from regular expression syntax:
* `1`: 1..1
* `?`: 0..1
* `+`: 1..n
* `*`: 0..n
-* optional properties for attributes :
+* optional properties for attributes:
- - `unique` : boolean indicating if the value of the attribute has to be unique
+ - `unique`: boolean indicating if the value of the attribute has to be unique
or not within all entities of the same type (false by default)
- - `indexed` : boolean indicating if an index needs to be created for this
+ - `indexed`: boolean indicating if an index needs to be created for this
attribute in the database (false by default). This is useful only if
you know that you will have to run numerous searches on the value of this
attribute.
- - `default` : default value of the attribute. In case of date types, the values
+ - `default`: default value of the attribute. In case of date types, the values
which could be used correspond to the RQL keywords `TODAY` and `NOW`.
-* optional properties of type `String` :
+* optional properties for type `String` attributes:
- - `fulltextindexed` : boolean indicating if the attribute is part of
+ - `fulltextindexed`: boolean indicating if the attribute is part of
the full text index (false by default) (*applicable on the type `Byte`
as well*)
- - `internationalizable` : boolean indicating if the value of the attribute
+ - `internationalizable`: boolean indicating if the value of the attribute
is internationalizable (false by default)
-* optional properties for relations :
+* optional properties for relations:
- - `composite` : string indicating that the subject (composite == 'subject')
+ - `composite`: string indicating that the subject (composite == 'subject')
is composed of the objects of the relations. For the opposite case (when
the object is composed of the subjects of the relation), we just set
'object' as value. The composition implies that when the relation
is deleted (so when the composite is deleted, at least), the composed are also deleted.
- - `fti_container`: XXX feed me
+ - `fulltext_container`: string indicating if the value if the full text
+ indexation of the entity on one end of the relation should be used
+ to find the entity on the other end. The possible values are
+ 'subject' or 'object'. For instance the use_email relation has
+ that property set to 'subject', since when performing a full text
+ search people want to find the entity using an email address, and not
+ the entity representing the email address.
Constraints
```````````
-By default, the available constraint types are :
+By default, the available constraint types are:
General Constraints
......................
-* `SizeConstraint` : allows to specify a minimum and/or maximum size on
+* `SizeConstraint`: allows to specify a minimum and/or maximum size on
string (generic case of `maxsize`)
-* `BoundConstraint` : allows to specify a minimum and/or maximum value on
+* `BoundConstraint`: allows to specify a minimum and/or maximum value on
numeric types
-* `UniqueConstraint` : identical to "unique=True"
+* `UniqueConstraint`: identical to "unique=True"
-* `StaticVocabularyConstraint` : identical to "vocabulary=(...)"
+* `StaticVocabularyConstraint`: identical to "vocabulary=(...)"
XXX Attribute, TODAY, NOW
@@ -159,39 +168,19 @@
displayed when the constraint fails. As RQLVocabularyConstraint never fails the
third argument is not available.
-* `RQLConstraint` : allows to specify a RQL query that has to be satisfied
+* `RQLConstraint`: allows to specify a RQL query that has to be satisfied
by the subject and/or the object of relation. In this query the variables
- `S` and `O` are reserved for the entities subject and object of the
- relation.
+ `S` and `O` are reserved for the relation subject and object entities.
-* `RQLVocabularyConstraint` : similar to the previous type of constraint except
+* `RQLVocabularyConstraint`: similar to the previous type of constraint except
that it does not express a "strong" constraint, which means it is only used to
restrict the values listed in the drop-down menu of editing form, but it does
not prevent another entity to be selected.
-* `RQLUniqueConstraint` : allows to the specify a RQL query that ensure that an
+* `RQLUniqueConstraint`: allows to the specify a RQL query that ensure that an
attribute is unique in a specific context. The Query must **never** return more
than a single result to be satisfied. In this query the variables `S` is
- reserved for the entity subject of the relation. The other variable should be
- specified with the second constructor argument (mainvars). This constraints
- should be used when UniqueConstraint doesn't fit. Here is a simple example ::
-
- # Check that in the same Workflow each state's name is unique. Using
- # UniqueConstraint (or unique=True) here would prevent states in different
- # workflows to have the same name.
-
- # With: State S, Workflow W, String N ; S state_of W, S name N
-
- RQLUniqueConstraint('S name N, S state_of WF, Y state_of WF, Y name N',
- mainvars='Y',
- msg=_('workflow already have a state of that name'))
-
-
-
-* `RQLUniqueConstraint` : allows to the specify a RQL query that ensure that an
- attribute is unique in a specific context. The Query must **never** return more
- than a single result to be satisfied. In this query the variables `S` is
- reserved for the entity subject of the relation. The other variable should be
+ reserved for the relation subject entity. The other variables should be
specified with the second constructor argument (mainvars). This constraints
should be used when UniqueConstraint doesn't fit. Here is a simple example ::
@@ -215,7 +204,7 @@
The security model
~~~~~~~~~~~~~~~~~~
-The security model of `cubicWeb` is based on `Access Control List`.
+The security model of `CubicWeb` is based on `Access Control List`.
The main principles are:
* users and groups of users
@@ -225,24 +214,25 @@
For *CubicWeb* in particular:
-* we associate rights at the enttities/relations schema level
-* for each entity, we distinguish four kind of permissions: read,
- add, update and delete
-* for each relation, we distinguish three kinds of permissions: read,
- add and delete (we can not modify a relation)
-* the basic groups are: Administrators, Users and Guests
-* by default, users belong to the group Users
-* there is a virtual group called `Owners` to which we
- can associate only deletion and update permissions
-* we can not add users to the `Owners` group, they are
- implicitly added to it according to the context of the objects
- they own
-* the permissions of this group are only checked on update/deletion
- actions if all the other groups the user belongs to does not provide
- those permissions
+* we associate rights at the entities/relations schema level
+* for each entity, we distinguish four kinds of permissions: `read`,
+ `add`, `update` and `delete`
+* for each relation, we distinguish three kinds of permissions: `read`,
+ `add` and `delete` (it is not possible to `modify` a relation)
+* the default groups are: `administrators`, `users` and `guests`
+* by default, users belong to the `users` group
+* there is a virtual group called `owners` to which we
+ can associate only `delete` and `update` permissions
+
+ * we can not add users to the `Owners` group, they are
+ implicitly added to it according to the context of the objects
+ they own
+ * the permissions of this group are only checked on `update`/`delete`
+ actions if all the other groups the user belongs to do not provide
+ those permissions
Setting permissions is done with the attribute `__permissions__` of entities and
-relation types. It defines a dictionary where the keys are the access types
+relation types. The value of this attribute is a dictionary where the keys are the access types
(action), and the values are the authorized groups or expressions.
For an entity type, the possible actions are `read`, `add`, `update` and
@@ -252,7 +242,7 @@
For each access type, a tuple indicates the name of the authorized groups and/or
one or multiple RQL expressions to satisfy to grant access. The access is
-provided if the user is in one of the listed groups or one of if the RQL condition
+provided if the user is in one of the listed groups or if one of the RQL condition
is satisfied.
The standard user groups
@@ -264,14 +254,14 @@
* `managers`
-* `owners` : virtual group corresponding to the entity's owner.
+* `owners`: virtual group corresponding to the entity's owner.
This can only be used for the actions `update` and `delete` of an entity
type.
It is also possible to use specific groups if they are defined in the
-precreate of the cube (``migration/precreate.py``). Defining groups in
-postcreate or even later makes them NOT available for security
-purposes (in this case, an `sync_schema_props_perms` command have to
+precreate script of the cube (``migration/precreate.py``). Defining groups in
+postcreate script or later makes them unavailable for security
+purposes (in this case, an `sync_schema_props_perms` command has to
be issued in a CubicWeb shell).
@@ -280,13 +270,13 @@
It is possible to define RQL expression to provide update permission
(`add`, `delete` and `update`) on relation and entity types.
-RQL expression for entity type permission :
+RQL expression for entity type permission:
* you have to use the class `ERQLExpression`
* the used expression corresponds to the WHERE statement of an RQL query
-* in this expression, the variables X and U are pre-defined references
+* in this expression, the variables `X` and `U` are pre-defined references
respectively on the current entity (on which the action is verified) and
on the user who send the request
@@ -297,19 +287,19 @@
to this variable
For RQL expressions on a relation type, the principles are the same except
-for the following :
+for the following:
* you have to use the class `RRQLExpression` in the case of a non-final relation
-* in the expression, the variables S, O and U are pre-defined references
+* in the expression, the variables `S`, `O` and `U` are pre-defined references
to respectively the subject and the object of the current relation (on
which the action is being verified) and the user who executed the query
* we can also define rights over attributes of an entity (non-final relation),
- knowing that :
+ knowing that:
- to define RQL expression, we have to use the class `ERQLExpression`
- in which X represents the entity the attribute belongs to
+ in which `X` represents the entity the attribute belongs to
- the permissions `add` and `delete` are equivalent. Only `add`/`read`
are actually taken in consideration.
@@ -333,7 +323,7 @@
Use of RQL expression for reading rights
````````````````````````````````````````
-The principles are the same but with the following restrictions :
+The principles are the same but with the following restrictions:
* we can not use `RRQLExpression` on relation types for reading
@@ -348,19 +338,28 @@
Entity type definition
~~~~~~~~~~~~~~~~~~~~~~
-An entity type is defined by a Python class which inherits from `EntityType`.
-The class definition contains the description of attributes and relations
-for the defined entity type.
-The class name corresponds to the entity type name. It is exepected to be
-defined in the module ``mycube.schema``.
+An entity type is defined by a Python class which inherits from
+:class:`yams.buildobjs.EntityType`. The class definition contains the
+description of attributes and relations for the defined entity type.
+The class name corresponds to the entity type name. It is expected to
+be defined in the module ``mycube.schema``.
+
+:Note on schema definition:
+
+ The code in ``mycube.schema`` is not meant to be executed. The class
+ EntityType mentioned above is different from the EntitySchema class
+ described in the previous chapter. EntityType is a helper class to
+ make Entity definition easier. Yams will process EntityType classes
+ and create EntitySchema instances from these class definitions. Similar
+ manipulation happen for relations.
When defining a schema using python files, you may use the following shortcuts:
-- `required` : boolean indicating if the attribute is required, eg subject cardinality is '1'
+- `required`: boolean indicating if the attribute is required, ed subject cardinality is '1'
-- `vocabulary` : specify static possible values of an attribute
+- `vocabulary`: specify static possible values of an attribute
-- `maxsize` : integer providing the maximum size of a string (no limit by default)
+- `maxsize`: integer providing the maximum size of a string (no limit by default)
For example:
@@ -382,6 +381,20 @@
birth and a relation that connects a `Person` to another entity of type
`Company` through the semantic `works_for`.
+:Naming convention:
+
+ Entity class names must start with an uppercase letter. The common
+ usage is to use ``CamelCase`` names.
+
+ Attribute and relation names must start with a lowercase letter. The
+ common usage is to use ``underscore_separated_words``. Attribute and
+ relation names starting with a single underscore are permitted, to
+ denote a somewhat "protected" or "private" attribute.
+
+ In any case, identifiers starting with "CW" or "cw" are reserved for
+ internal use by the framework.
+
+
The name of the Python attribute corresponds to the name of the attribute
or the relation in *CubicWeb* application.
@@ -390,29 +403,16 @@
attr_name = attr_type(properties)
where `attr_type` is one of the type listed above and `properties` is
-a list of the attribute needs to statisfy (see `Properties`_
+a list of the attribute needs to satisfy (see `Properties`_
for more details).
-
-* relations can be defined by using `ObjectRelation` or `SubjectRelation`.
- The first argument of `SubjectRelation` or `ObjectRelation` gives respectively
- the object/subject entity type of the relation. This could be :
-
- * a string corresponding to an entity type
-
- * a tuple of string corresponding to multiple entity types
-
- * special string such as follows :
-
- - "**" : all types of entities
- - "*" : all types of non-meta entities
- - "@" : all types of meta entities but not system entities (e.g. used for
- the basic schema description)
-
* it is possible to use the attribute `meta` to flag an entity type as a `meta`
(e.g. used to describe/categorize other entities)
-*Note* : if you end up with an `if` in the definition of your entity, this probably
+.. XXX the paragraph below needs clarification and / or moving out in
+.. another place
+
+*Note*: if you end up with an `if` in the definition of your entity, this probably
means that you need two separate entities that implement the `ITree` interface and
get the result from `.children()` which ever entity is concerned.
@@ -440,20 +440,49 @@
subject = '*'
object = 'CWUser'
-In the case of simultaneous relations definitions, `subject` and `object`
-can both be equal to the value of the first argument of `SubjectRelation`
-and `ObjectRelation`.
+If provided, the `subject` and `object` attributes denote the subject
+and object of the various relation definitions related to the relation
+type. Allowed values for these attributes are:
+
+* a string corresponding to an entity type
+* a tuple of string corresponding to multiple entity types
+* special string such as follows:
+
+ - "**": all types of entities
+ - "*": all types of non-meta entities
+ - "@": all types of meta entities but not system entities (e.g. used for
+ the basic schema description)
When a relation is not inlined and not symmetrical, and it does not require
-specific permissions, its definition (by using `SubjectRelation` and
-`ObjectRelation`) is all we need.
+specific permissions, it can be defined using a `SubjectRelation`
+attribute in the EntityType class. The first argument of `SubjectRelation` gives
+the entity type for the object of the relation.
+
+:Naming convention:
+
+ Although this way of defining relations uses a Python class, the
+ naming convention defined earlier prevails over the PEP8 conventions
+ used in the framework: relation type class names use
+ ``underscore_separated_words``.
+:Historical note:
+
+ It has been historically possible to use `ObjectRelation` which
+ defines a relation in the opposite direction. This feature is soon to be
+ deprecated and therefore should not be used in newly written code.
+
+:Future deprecation note:
+
+ In an even more remote future, it is quite possible that the
+ SubjectRelation shortcut will become deprecated, in favor of the
+ RelationType declaration which offers some advantages in the context
+ of reusable cubes.
Definition of permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~
The entity type `CWPermission` from the standard library
allows to build very complex and dynamic security architectures. The schema of
-this entity type is as follow :
+this entity type is as follow:
.. sourcecode:: python
@@ -461,12 +490,12 @@
"""entity type that may be used to construct some advanced security configuration
"""
name = String(required=True, indexed=True, internationalizable=True, maxsize=100)
- require_group = SubjectRelation('CWGroup', cardinality='+*',
+ require_group = SubjectRelation('CWGroup', cardinality='+*',
description=_('groups to which the permission is granted'))
- require_state = SubjectRelation('State',
+ require_state = SubjectRelation('State',
description=_("entity's state in which the permission is applicable"))
# can be used on any entity
- require_permission = ObjectRelation('**', cardinality='*1', composite='subject',
+ require_permission = ObjectRelation('**', cardinality='*1', composite='subject',
description=_("link a permission to the entity. This "
"permission should be used in the security "
"definition of the entity's type to be useful."))
@@ -502,7 +531,7 @@
This configuration indicates that an entity `CWPermission` named
"add_version" can be associated to a project and provides rights to create
-new versions on this project to specific groups. It is important to notice that :
+new versions on this project to specific groups. It is important to notice that:
* in such case, we have to protect both the entity type "Version" and the relation
associating a version to a project ("version_of")
--- a/doc/book/en/development/devcore/appobject.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-
-
-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 recording time, the following attributes are dynamically added to
-the *subclasses*:
-
-* `vreg`, the `vregistry` of the instance
-* `schema`, the instance schema
-* `config`, the instance configuration
-
-We also find on instances, the following attributes:
-
-* ._cw`, `Request` instance
-* `rset`, the *result set* associated to the object if necessary
-
-:URL handling:
- * `build_url(*args, **kwargs)`, returns an absolute URL based on the
- given arguments. The *controller* supposed to handle the response,
- can be specified through the first positional parameter (the
- connection is theoretically done automatically :).
-
-:Data manipulation:
-
- * `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
- date time according to instance's configuration
- * `format_time(time)` returns a string for a date time according to
- instance's configuration
-
-:And more...:
-
- * `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 must write: ::
-
- class Truc(PrimaryView):
- def f(self, arg1):
- super(Truc, self).f(arg1)
--- a/doc/book/en/development/devcore/cwconfig.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/devcore/cwconfig.rst Tue Apr 06 19:08:07 2010 +0200
@@ -1,5 +1,5 @@
:mod:`Configuration <cubicweb.cwconfig>`
----------------------------------------
-.. automodule:: cubicweb.cwconfig
- :members:
+.. .. automodule:: cubicweb.cwconfig
+.. :members:
--- a/doc/book/en/development/devcore/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/devcore/index.rst Tue Apr 06 19:08:07 2010 +0200
@@ -4,9 +4,7 @@
.. toctree::
:maxdepth: 1
- vreg.rst
- appobject.rst
- selectors.rst
dbapi.rst
+ reqbase.rst
cwconfig.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/devcore/reqbase.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,30 @@
+
+Those are methods you'll find on both request objects and on repository session:
+
+:URL handling:
+ * `build_url(*args, **kwargs)`, returns an absolute URL based on the
+ given arguments. The *controller* supposed to handle the response,
+ can be specified through the first positional parameter (the
+ connection is theoretically done automatically :).
+:Data formatting:
+ * `format_date(date, date_format=None, time=False)` returns a string for a
+ date time according to instance's configuration
+
+ * `format_time(time)` returns a string for a date time according to
+ instance's configuration
+
+:And more...:
+
+ * `tal_render(template, variables)`, renders a precompiled page template with
+ variables in the given dictionary as context
+
+
+Result set methods:
+
+ * `get_entity(row, col)`, returns the entity corresponding to the data position
+ in the *result set*
+
+ * `complete_entity(row, col, skip_bytes=True)`, is equivalent to `get_entity` but
+ also call the method `complete()` on the entity before returning it
+
+
--- a/doc/book/en/development/devcore/selectors.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-Base selectors
---------------
-
-Selectors are scoring functions that are called by the registry to tell whenever
-an appobject can be selected in a given context. Selector sets are for instance
-the glue that tie views to the data model. Using them appropriately is an
-essential part of the construction of well behaved cubes.
-
-Of course you may have to write your own set of selectors as your needs grows and
-you get familiar with the framework (see :ref:`CustomSelectors`).
-
-Here is a description of generic selectors provided by CubicWeb that should suit
-most of your needs.
-
-Bare selectors
-~~~~~~~~~~~~~~
-Those selectors are somewhat dumb, which doesn't mean they're not (very) useful.
-
-.. autoclass:: cubicweb.appobject.yes
-.. autoclass:: cubicweb.selectors.match_kwargs
-.. autoclass:: cubicweb.selectors.appobject_selectable
-
-
-Result set selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for a result set in the context ('rset' argument or
-the input context) and match or not according to its shape. Some of these
-selectors have different behaviour if a particular cell of the result set is
-specified using 'row' and 'col' arguments of the input context or not.
-
-.. autoclass:: cubicweb.selectors.none_rset
-.. autoclass:: cubicweb.selectors.any_rset
-.. autoclass:: cubicweb.selectors.nonempty_rset
-.. autoclass:: cubicweb.selectors.empty_rset
-.. autoclass:: cubicweb.selectors.one_line_rset
-.. autoclass:: cubicweb.selectors.multi_lines_rset
-.. autoclass:: cubicweb.selectors.multi_columns_rset
-.. autoclass:: cubicweb.selectors.paginated_rset
-.. autoclass:: cubicweb.selectors.sorted_rset
-.. autoclass:: cubicweb.selectors.one_etype_rset
-.. autoclass:: cubicweb.selectors.multi_etypes_rset
-
-
-Entity selectors
-~~~~~~~~~~~~~~~~
-Those selectors are looking for either an `entity` argument in the input context,
-or entity found in the result set ('rset' argument or the input context) and
-match or not according to entity's (instance or class) properties.
-
-.. autoclass:: cubicweb.selectors.non_final_entity
-.. autoclass:: cubicweb.selectors.implements
-.. autoclass:: cubicweb.selectors.score_entity
-.. autoclass:: cubicweb.selectors.rql_condition
-.. autoclass:: cubicweb.selectors.relation_possible
-.. autoclass:: cubicweb.selectors.partial_relation_possible
-.. autoclass:: cubicweb.selectors.has_related_entities
-.. autoclass:: cubicweb.selectors.partial_has_related_entities
-.. autoclass:: cubicweb.selectors.has_permission
-.. autoclass:: cubicweb.selectors.has_add_permission
-
-
-Logged user selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for properties of the user issuing the request.
-
-.. autoclass:: cubicweb.selectors.anonymous_user
-.. autoclass:: cubicweb.selectors.authenticated_user
-.. autoclass:: cubicweb.selectors.match_user_groups
-
-
-Web request selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for properties of *web* request, they can not be
-used on the data repository side.
-
-.. autoclass:: cubicweb.selectors.match_form_params
-.. autoclass:: cubicweb.selectors.match_search_state
-.. autoclass:: cubicweb.selectors.match_context_prop
-.. autoclass:: cubicweb.selectors.match_view
-.. autoclass:: cubicweb.selectors.primary_view
-.. autoclass:: cubicweb.selectors.specified_etype_implements
-
-
-Other selectors
-~~~~~~~~~~~~~~~
-.. autoclass:: cubicweb.selectors.match_transition
-
-You'll also find some other (very) specific selectors hidden in other modules
-than :mod:`cubicweb.selectors`.
--- a/doc/book/en/development/devcore/vreg.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,220 +0,0 @@
-The VRegistry
---------------
-
-The recording process on startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Details of the recording process
-````````````````````````````````
-
-.. index::
- vregistry: registration_callback
-
-On startup, |cubicweb| have to fill the vregistry with appobjects defined
-in its library and in cubes used by the instance. Appobjects from the library
-are loaded first, then appobjects provided by cubes are loaded in an ordered
-way (e.g. if your cube depends on an other, appobjects from the dependancy will
-be loaded first). Cube's modules or packages where appobject are looked at is explained
-in :ref:`cubelayout`.
-
-For each module:
-
-* by default all objects are registered automatically
-
-* if some objects have to replace other objects or be included only if a
- condition is true, you'll have to define a `registration_callback(vreg)`
- function in your module and explicitly register *all objects* in this
- module, using the vregistry api defined below.
-
-.. note::
- Once the function `registration_callback(vreg)` is implemented, all the objects
- have to be explicitly registered as it disables the automatic object registering.
-
-
-API d'enregistrement des objets
-```````````````````````````````
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
-
-
-Examples
-````````
-.. sourcecode:: python
-
- # web/views/basecomponents.py
- def registration_callback(vreg):
- # register everything in the module except SeeAlsoComponent
- vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
- # conditionally register SeeAlsoVComponent
- if 'see_also' in vreg.schema:
- vreg.register(SeeAlsoVComponent)
-
- # goa/appobjects/sessions.py
- def registration_callback(vreg):
- vreg.register(SessionsCleaner)
- # replace AuthenticationManager by GAEAuthenticationManager
- vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
- # replace PersistentSessionManager by GAEPersistentSessionManager
- vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
-
-
-Runtime objects selection
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Using and combining existant selectors
-``````````````````````````````````````
-
-The object's selector is defined by its `__select__` class attribute.
-
-When two selectors are combined using the `&` operator (formerly `chainall`), it
-means that both should return a positive score. On success, the sum of scores is returned.
-
-When two selectors are combined using the `|` operator (former `chainfirst`), it
-means that one of them should return a positive score. On success, the first
-positive score is returned.
-
-You can also "negate" a selector by precedeing it by the `~` operator.
-
-Of course you can use paren to balance expressions.
-
-
-For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
-`__registry__ = 'view'`) for a result set containing a `Card` entity, 2 objects
-will probably be selectable:
-
-* the default primary view (`__select__ = implements('Any')`), meaning
- that the object is selectable for any kind of entity type
-
-* the specific `Card` primary view (`__select__ = implements('Card')`,
- meaning that the object is selectable for Card entities
-
-Other primary views specific to other entity types won't be selectable
-in this case. Among selectable objects, the implements selector will
-return a higher score than the second view since it's more specific,
-so it will be selected as expected.
-
-
-Example
-````````
-
-The goal: when on a Blog, one wants the RSS link to refer to blog
-entries, not to the blog entity itself.
-
-To do that, one defines a method on entity classes that returns the
-RSS stream url for a given entity. The default implementation on
-AnyEntity and a specific implementation on Blog will do what we want.
-
-But when we have a result set containing several Blog entities (or
-different entities), we don't know on which entity to call the
-aforementioned method. In this case, we keep the current behaviour
-(e.g : call to limited_rql).
-
-Hence we have two cases here, one for a single-entity rsets, the other
-for multi-entities rsets.
-
-In web/views/boxes.py lies the RSSIconBox class. Look at its selector ::
-
- class RSSIconBox(ExtResourcesBoxTemplate):
- """just display the RSS icon on uniform result set"""
- __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
-
-It takes into account:
-
-* the inherited selection criteria (one has to look them up in the
- class hierarchy to know the details)
-
-* non_final_entity, which filters on rsets containing non final
- entities (a 'final entity' being synonym for entity attribute)
-
-This matches our second case. Hence we have to provide a specific
-component for the first case::
-
- class EntityRSSIconBox(RSSIconBox):
- """just display the RSS icon on uniform result set for a single entity"""
- __select__ = RSSIconBox.__select__ & one_line_rset()
-
-Here, one adds the one_line_rset selector, which filters result sets
-of size 1. When one chains selectors, the final score is the sum of
-the score of each individual selector (unless one of them returns 0,
-in which case the object is non selectable). Thus, on a multiple
-entities selector, one_line_rset makes the EntityRSSIconBox class non
-selectable. For an rset with one entity, the EntityRSSIconBox class
-will have a higher score then RSSIconBox, which is what we wanted.
-
-Of course, once this is done, you have to:
-
-* fill in the call method of EntityRSSIconBox
-
-* provide the default implementation of the method returning the RSS
- stream url on AnyEntity
-
-* redefine this method on Blog.
-
-When to use selectors?
-``````````````````````
-
-Selectors are to be used whenever arises the need of dispatching on the shape or
-content of a result set or whatever else context (value in request form params,
-authenticated user groups, etc...). That is, almost all the time.
-
-XXX add and example of a single view w/ big "if" inside splitted into two views
-with appropriate selectors.
-
-
-.. CustomSelectors_
-
-Defining your own selectors
-```````````````````````````
-.. autoclass:: cubicweb.appobject.Selector
- :members: __call__
-
-.. autofunction:: cubicweb.appobject.objectify_selector
-.. autofunction:: cubicweb.selectors.lltrace
-
-Selectors __call__ should *always* return a positive integer, and shall never
-return `None`.
-
-Useful abstract base classes for 'entity' selectors:
-
-.. autoclass:: cubicweb.selectors.EClassSelector
-.. autoclass:: cubicweb.selectors.EntitySelector
-
-
-Debugging
-`````````
-
-Once in a while, one needs to understand why a view (or any AppObject)
-is, or is not selected appropriately. Looking at which selectors fired
-(or did not) is the way. There exists a traced_selection context
-manager to help with that, *if you're running your instance in debug mode*.
-
-Here is an example:
-
-.. sourcecode:: python
-
- from cubicweb.selectors import traced_selection
- with traced_selection():
- mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
-
-Don't forget the 'from __future__ import with_statement' at the module
-top-level if you're using python 2.5.
-
-This will yield additional WARNINGs in the logs, like this::
-
- 2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
-
-You can also give to traced_selection the registry ids of objects on which to debug
-you want to debug selection ('wfhistory' in the example above).
-
-Also, if you're using python 2.4, which as no 'with' yet, you'll have to to it
-the following way:
-
-.. sourcecode:: python
-
- from cubicweb import selectors
- selectors.TRACED_OIDS = ('wfhistory',)
- mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
- selectors.TRACED_OIDS = ()
--- a/doc/book/en/development/devweb/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/devweb/index.rst Tue Apr 06 19:08:07 2010 +0200
@@ -12,7 +12,6 @@
property
rtags
views
- gettingdata
form
facets
httpcaching
--- a/doc/book/en/development/devweb/internationalization.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/devweb/internationalization.rst Tue Apr 06 19:08:07 2010 +0200
@@ -15,7 +15,7 @@
* in your Python code and cubicweb-tal templates : mark translatable strings
-* in your instance : handle the translation catalog
+* in your instance : handle the translation catalog, edit translations
String internationalization
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -50,8 +50,9 @@
itself, but not its translation (it's actually another name for the
`unicode` builtin).
-In the other hand the request's method `self._cw._` is meant to retrieve the
-proper translation of translation strings in the requested language.
+In the other hand the request's method `self._cw._` is also meant to
+retrieve the proper translation of translation strings in the
+requested language.
Finally you can also use the `__` attribute of request object to get a
translation for a string *which should not itself added to the catalog*,
@@ -62,14 +63,15 @@
In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in
messages catalogs.
-
Translations in cubicweb-tal template can also be done with TAL tags
`i18n:content` and `i18n:replace`.
-
If you need to add messages on top of those that can be found in the source,
you can create a file named `i18n/static-messages.pot`.
+You could put there messages not found in the python sources or
+overrides for some messages of used cubes.
+
Generated string
````````````````
@@ -80,20 +82,20 @@
For exemple the following schema ::
Class EntityA(EntityType):
- relationa2b = SubjectRelation('EntityB')
+ relation_a2b = SubjectRelation('EntityB')
class EntityB(EntityType):
pass
May generate the following message ::
- add Execution has_export File subject
+ add EntityA relation_a2b EntityB subject
This message will be used in views of ``EntityA`` for creation of a new
``EntityB`` with a preset relation ``relation_a2b`` between the current
``EntityA`` and the new ``EntityB``. The opposite message ::
- add Execution has_export File object
+ add EntityA relation_a2b EntityB object
Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The
title of they respective creation form will be ::
@@ -105,8 +107,8 @@
In the translated string you can use ``%(linkto)s`` for reference to the source
``entity``.
-Handle the translation catalog
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Handling the translation catalog
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once the internationalization is done in your code, you need to populate and
update the translation catalog. Cubicweb provides the following commands for this
@@ -117,11 +119,11 @@
catalogs. Unless you actually work on the framework itself, you
don't need to use this command.
-* `i18ncube` updates the translation catalogs of *one particular
- cube* (or of all cubes). After this command is
- executed you must update the translation files *.po* in the "i18n"
- directory of your template. This command will of course not remove
- existing translations still in use.
+* `i18ncube` updates the translation catalogs of *one particular cube*
+ (or of all cubes). After this command is executed you must update
+ the translation files *.po* in the "i18n" directory of your
+ cube. This command will of course not remove existing translations
+ still in use. It will mark unused translation but not remove them.
* `i18ninstance` recompiles the translation catalogs of *one particular
instance* (or of all instances) after the translation catalogs of
@@ -134,6 +136,7 @@
Example
```````
+
You have added and/or modified some translation strings in your cube
(after creating a new view or modifying the cube's schema for exemple).
To update the translation catalogs you need to do:
@@ -143,3 +146,77 @@
3. `hg ci -m "updated i18n catalogs"`
4. `cubicweb-ctl i18ninstance <myinstance>`
+Editing po files
+~~~~~~~~~~~~~~~~
+
+Using a PO aware editor
+````````````````````````
+
+Many tools exist to help maintain .po (PO) files. Common editors or
+development environment provides modes for these. One can also find
+dedicated PO files editor, such as `poedit`_.
+
+.. _`poedit`: http://www.poedit.net/
+
+While usage of such a tool is commendable, PO files are perfectly
+editable with a (unicode aware) plain text editor. It is also useful
+to know their structure for troubleshooting purposes.
+
+Structure of a PO file
+``````````````````````
+
+In this section, we selectively quote passages of the `GNU gettext`_
+manual chapter on PO files, available there::
+
+ http://www.gnu.org/software/hello/manual/gettext/PO-Files.html
+
+One PO file entry has the following schematic structure::
+
+ white-space
+ # translator-comments
+ #. extracted-comments
+ #: reference...
+ #, flag...
+ #| msgid previous-untranslated-string
+ msgid untranslated-string
+ msgstr translated-string
+
+
+A simple entry can look like this::
+
+ #: lib/error.c:116
+ msgid "Unknown system error"
+ msgstr "Error desconegut del sistema"
+
+It is also possible to have entries with a context specifier. They
+look like this::
+
+ white-space
+ # translator-comments
+ #. extracted-comments
+ #: reference...
+ #, flag...
+ #| msgctxt previous-context
+ #| msgid previous-untranslated-string
+ msgctxt context
+ msgid untranslated-string
+ msgstr translated-string
+
+
+The context serves to disambiguate messages with the same
+untranslated-string. It is possible to have several entries with the
+same untranslated-string in a PO file, provided that they each have a
+different context. Note that an empty context string and an absent
+msgctxt line do not mean the same thing.
+
+Contexts and CubicWeb
+`````````````````````
+
+CubicWeb PO files have both non-contextual and contextual msgids.
+
+Contextual entries are automatically used in some cases. For instance,
+entity.dc_type(), eschema.display_name(req) or display_name(etype,
+req, form, context) methods/function calls will use them.
+
+It is also possible to explicitly use the with _cw.pgettext(context,
+msgid).
--- a/doc/book/en/development/devweb/js.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/devweb/js.rst Tue Apr 06 19:08:07 2010 +0200
@@ -54,12 +54,224 @@
ajax request, otherwise the document itself for standard HTTP
requests.
+Important AJAX APIS
+~~~~~~~~~~~~~~~~~~~
-Overview of what's available
+* `jQuery.fn.loadxhtml` is an important extension to jQuery which
+ allow proper loading and in-place DOM update of xhtml views. It is
+ suitably augmented to trigger necessary events, and process CubicWeb
+ specific elements such as the facet system, fckeditor, etc.
+
+* `asyncRemoteExec` and `remoteExec` are the base building blocks for
+ doing arbitrary async (resp. sync) communications with the server
+
+A simple example with asyncRemoteExec
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the python side, we have to extend the BaseController class. The
+@jsonize decorator ensures that the `return value` of the method is
+encoded as JSON data. By construction, the JSonController inputs
+everything in JSON format.
+
+.. sourcecode: python
+
+ from cubicweb.web.views.basecontrollers import JSonController, jsonize
+
+ @monkeypatch(JSonController)
+ @jsonize
+ def js_say_hello(self, name):
+ return u'hello %s' % name
+
+In the javascript side, we do the asynchronous call. Notice how it
+creates a `deferred` object. Proper treatment of the return value or
+error handling has to be done through the addCallback and addErrback
+methods.
+
+.. sourcecode: javascript
+
+ function async_hello(name) {
+ var deferred = asyncRemoteExec('say_hello', name);
+ deferred.addCallback(function (response) {
+ alert(response);
+ });
+ deferred.addErrback(function () {
+ alert('something fishy happened');
+ });
+ }
+
+ function sync_hello(name) {
+ alert( remoteExec('say_hello', name) );
+ }
+
+A simple example with loadxhtml
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here we are concerned with the retrieval of a specific view to be
+injected in the live DOM. The view will be of course selected
+server-side using an entity eid provided by the client side.
+
+.. sourcecode: python
+
+ from cubicweb import typed_eid
+ from cubicweb.web.views.basecontrollers import JSonController, xhtmlize
+
+ @monkeypatch(JSonController)
+ @xhtmlize
+ def js_frob_status(self, eid, frobname):
+ entity = self._cw.entity_from_eid(typed_eid(eid))
+ return entity.view('frob', name=frobname)
+
+.. sourcecode: javascript
+
+ function update_some_div(divid, eid, frobname) {
+ var params = {fname:'frob_status', eid: eid, frobname:frobname};
+ jQuery('#'+divid).loadxhtml(JSON_BASE_URL, params, 'post');
+ }
+
+In this example, the url argument is the base json url of a cube
+instance (it should contain something like
+`http://myinstance/json?`). The actual JSonController method name is
+encoded in the `params` dictionnary using the `fname` key.
+
+A more real-life example from CubicWeb
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A frequent use case of Web 2 applications is the delayed (or
+on-demand) loading of pieces of the DOM. This is typically achieved
+using some preparation of the initial DOM nodes, jQuery event handling
+and proper use of loadxhtml.
+
+We present here a skeletal version of the mecanism used in CubicWeb
+and available in web/views/tabs.py, in the `LazyViewMixin` class.
+
+.. sourcecode: python
+
+ def lazyview(self, vid, rql=None):
+ """ a lazy version of wview """
+ w = self.w
+ self._cw.add_js('cubicweb.lazy.js')
+ urlparams = {'vid' : vid, 'fname' : 'view'}
+ if rql is not None:
+ urlparams['rql'] = rql
+ w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % (
+ vid, xml_escape(self._cw.build_url('json', **urlparams))))
+ w(u'</div>')
+ self._cw.add_onload(u"""
+ jQuery('#lazy-%(vid)s').bind('%(event)s', function() {
+ load_now('#lazy-%(vid)s');});"""
+ % {'event': 'load_%s' % vid, 'vid': vid})
+
+This creates a `div` with an specific event associated to it.
+
+The full version deals with:
+
+* optional parameters such as an entity eid, an rset
+
+* the ability to further reload the fragment
+
+* the ability to display a spinning wheel while the fragment is still
+ not loaded
+
+* handling of browsers that do not support ajax (search engines,
+ text-based browsers such as lynx, etc.)
+
+The javascript side is quite simple, due to loadxhtml awesomeness.
+
+.. sourcecode: javascript
+
+ function load_now(eltsel) {
+ var lazydiv = jQuery(eltsel);
+ lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl'));
+ }
+
+This is all significantly different of the previous `simple example`
+(albeit this example actually comes from real-life code).
+
+Notice how the `cubicweb:loadurl` is used to convey the url
+information. The base of this url is similar to the global javascript
+JSON_BASE_URL. According to the pattern described earlier,
+the `fname` parameter refers to the standard `js_view` method of the
+JSonController. This method renders an arbitrary view provided a view
+id (or `vid`) is provided, and most likely an rql expression yielding
+a result set against which a proper view instance will be selected.
+
+The `cubicweb:loadurl` is one of the 29 attributes extensions to XHTML
+in a specific cubicweb namespace. It is a means to pass information
+without breaking HTML nor XHTML compliance and without resorting to
+ungodly hacks.
+
+Given all this, it is easy to add a small nevertheless useful feature
+to force the loading of a lazy view (for instance, a very
+computation-intensive web page could be scinded into one fast-loading
+part and a delayed part).
+
+In the server side, a simple call to a javascript function is
+sufficient.
+
+.. sourcecode: python
+
+ def forceview(self, vid):
+ """trigger an event that will force immediate loading of the view
+ on dom readyness
+ """
+ self._cw.add_onload("trigger_load('%s');" % vid)
+
+The browser-side definition follows.
+
+.. sourcecode: javascript
+
+ function trigger_load(divid) {
+ jQuery('#lazy-' + divd).trigger('load_' + divid);
+ }
+
+
+Anatomy of a lodxhtml call
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The loadxhtml extension to jQuery accept many parameters with rich
+semantics. Let us detail these.
+
+* `url` (mandatory) should be a complete url, typically based on the
+ JSonController, but this is not strictly mandatory
+
+* `data` (optional) is a dictionnary of values given to the
+ controller specified through an `url` argument; some keys may have a
+ special meaning depending on the choosen controller (such as `fname`
+ for the JSonController); the `callback` key, if present, must refer
+ to a function to be called at the end of loadxhtml (more on this
+ below)
+
+* `reqtype` (optional) specifies the request method to be used (get or
+ post); if the argument is 'post', then the post method is used,
+ otherwise the get method is used
+
+* `mode` (optional) is one of `replace` (the default) which means the
+ loaded node will replace the current node content, `swap` to replace
+ the current node with the loaded node, and `append` which will
+ append the loaded node to the current node content
+
+
+About the `callback` option:
+
+* it is called with two parameters: the current node, and a list
+ containing the loaded (and post-processed node)
+
+* whenever is returns another function, this function is called in
+ turn with the same parameters as above
+
+This mecanism allows callback chaining.
+
+
+Javascript library: overview
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* jquery.* : jquery and jquery UI library
+* cubicweb.ajax.js : concentrates all ajax related facilities (it
+ extends jQuery with the loahxhtml function, provides a handfull of
+ high-level ajaxy operations like asyncRemoteExec, reloadComponent,
+ replacePageChunk, getDomFromResponse)
+
* cubicweb.python.js : adds a number of practical extension to stdanrd
javascript objects (on Date, Array, String, some list and dictionary
operations), and a pythonesque way to build classes. Defines a
@@ -69,11 +281,6 @@
in various other cubicweb javascript resources (baseuri, progress
cursor handling, popup login box, html2dom function, etc.)
-* cubicweb.ajax.js : concentrates all ajax related facilities (it
- extends jQuery with the loahxhtml function, provides a handfull of
- high-level ajaxy operations like asyncRemoteExec, reloadComponent,
- replacePageChunk, getDomFromResponse)
-
* cubicweb.widgets.js : provides a widget namespace and constructors
and helpers for various widgets (mainly facets and timeline)
@@ -83,5 +290,6 @@
* cubicweb.facets.js : used by the facets mechanism
-xxx massmailing, gmap, fckcwconfig, timeline-bundle, timeline-ext,
-calendar, goa, flotn tazy, tabs, bookmarks
+There is also javascript support for massmailing, gmap (google maps),
+fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over
+AppEngine), flot (charts drawing), tabs and bookmarks.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/entityclasses/application-logic.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,166 @@
+How to use entities objects
+---------------------------
+
+The previous chapters detailed the classes and methods available to
+the developper at the so-called `ORM`_ level. However they say little
+about the common patterns of usage of these objects.
+
+.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping
+
+Entities objects are used in the repository and web sides of
+CubicWeb. In the repository side of things, one should manipulate them
+in Hooks and Operations.
+
+Hooks and Operations provide support for the implementation of rules
+such as computed attributes, coherency invariants, etc (they play the
+same role as database triggers, but in a way that is independant of
+the actual data sources).
+
+So a lot of an application's business rules will be written in Hooks
+(or Operations).
+
+In the web side, views also typically operate using entity
+objects. Obvious entity methods for use in views are the dublin code
+method like dc_title, etc. For separation of concerns reasons, one
+should ensure no ui logic pervades the entities level, and also no
+business logic should creep into the views.
+
+In the duration of a transaction, entities objects can be instantiated
+many times, in views and hooks, even for the same database entity. For
+instance, in a classic CubicWeb deployment setup, the repository and
+the web frontend are separated process communicating over the
+wire. There is no way state can be shared between these processes
+(there is a specific API for that). Hence, it is not possible to use
+entity objects as messengers between these components of an
+application. It means that an attribute set as in `obj.x = 42`,
+whether or not x is actually an entity schema attribute, has a short
+life span, limited to the hook, operation or view within which the
+object was built.
+
+Setting an attribute or relation value can be done in the context of a
+Hook/Operation, using the obj.set_attributes(x=42) notation or a plain
+RQL SET expression.
+
+In views, it would be preferable to encapsulate the necessary logic in
+a method of the concerned entity class(es). But of course, this advice
+is also reasonnable for Hooks/Operations, though the separation of
+concerns here is less stringent than in the case of views.
+
+This leads to the practical role of entity objects: it's where an
+important part of the application logic lie (the other part being
+located in the Hook/Operations).
+
+Anatomy of an entity class
+--------------------------
+
+We can look now at a real life example coming from the `tracker`_
+cube. Let us begin to study the entities/project.py content.
+
+.. sourcecode:: python
+
+ class Project(TreeMixIn, AnyEntity):
+ __regid__ = 'Project'
+ __implements__ = AnyEntity.__implements__ + (ITree,)
+ fetch_attrs, fetch_order = fetch_config(('name', 'description',
+ 'description_format', 'summary'))
+
+ TICKET_DEFAULT_STATE_RESTR = 'S name IN ("created","identified","released","scheduled")'
+
+ tree_attribute = 'subproject_of'
+ parent_target = 'subject'
+ children_target = 'object'
+
+ def dc_title(self):
+ return self.name
+
+First we see that it uses an ITree interface and the TreeMixIn default
+implementation. The attributes `tree_attribute`, `parent_target` and
+`children_target` are used by the TreeMixIn code. This is typically
+used in views concerned with the representation of tree-like
+structures (CubicWeb provides several such views).
+
+It is important that the views themselves try not to implement this
+logic, not only because such views would be hardly applyable to other
+tree-like relations, but also because it is perfectly fine and useful
+to use such an interface in Hooks.
+
+In fact, Tree nature is a property of the data model that cannot be
+fully and portably expressed at the level of database entities (think
+about the transitive closure of the child relation). This is a further
+argument to implement it at entity class level.
+
+The `dc_title` method provides a (unicode string) value likely to be
+consummed by views, but note that here we do not care about output
+encodings. We care about providing data in the most universal format
+possible, because the data could be used by a web view (which would be
+responsible of ensuring XHTML compliance), or a console or file
+oriented output (which would have the necessary context about the
+needed byte stream encoding).
+
+The fetch_attrs, fetch_order class attributes are parameters of the
+`ORM`_ layer. They tell which attributes should be loaded at once on
+entity object instantiation (by default, only the eid is known, other
+attributes are loaded on demand), and which attribute is to be used to
+order the .related() and .unrelated() methods output.
+
+Finally, we can observe the big TICKET_DEFAULT_STATE_RESTR is a pure
+application domain piece of data. There is, of course, no limitation
+to the amount of class attributes of this kind.
+
+Let us now dig into more substantial pieces of code.
+
+.. sourcecode:: python
+
+ def latest_version(self, states=('published',), reverse=None):
+ """returns the latest version(s) for the project in one of the given
+ states.
+
+ when no states specified, returns the latest published version.
+ """
+ order = 'DESC'
+ if reverse is not None:
+ warn('reverse argument is deprecated',
+ DeprecationWarning, stacklevel=1)
+ if reverse:
+ order = 'ASC'
+ rset = self.versions_in_state(states, order, True)
+ if rset:
+ return rset.get_entity(0, 0)
+ return None
+
+ def versions_in_state(self, states, order='ASC', limit=False):
+ """returns version(s) for the project in one of the given states, sorted
+ by version number.
+
+ If limit is true, limit result to one version.
+ If reverse, versions are returned from the smallest to the greatest.
+ """
+ if limit:
+ order += ' LIMIT 1'
+ rql = 'Any V,N ORDERBY version_sort_value(N) %s ' \
+ 'WHERE V num N, V in_state S, S name IN (%s), ' \
+ 'V version_of P, P eid %%(p)s' % (order, ','.join(repr(s) for s in states))
+ return self._cw.execute(rql, {'p': self.eid})
+
+.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker/
+
+These few lines exhibit the important properties we want to outline:
+
+* entity code is concerned with the application domain
+
+* it is NOT concerned with database coherency (this is the realm of
+ Hooks/Operations); in other words, it assumes a coherent world
+
+* it is NOT concerned with end-user interfaces
+
+* however it can be used in both contexts
+
+* it does not create or manipulate the internal object's state
+
+* it plays freely with RQL expression as needed
+
+* it is not concerned with internationalization
+
+* it does not raise exceptions
+
+
--- a/doc/book/en/development/entityclasses/data-as-objects.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/entityclasses/data-as-objects.rst Tue Apr 06 19:08:07 2010 +0200
@@ -17,6 +17,7 @@
:Formatting and output generation:
* `view(vid, **kwargs)`, applies the given view to the entity
+ (and returns an unicode string)
* `absolute_url(**kwargs)`, returns an absolute URL to access the primary view
of an entity
@@ -32,22 +33,30 @@
* `as_rset()`, converts the entity into an equivalent result set simulating the
request `Any X WHERE X eid _eid_`
- * `complete(skip_bytes=True)`, executes a request that recovers all at once
- all the missing attributes of an entity
+ * `complete(skip_bytes=True)`, executes a request that recovers at
+ once all the missing attributes of an entity
* `get_value(name)`, returns the value associated to the attribute name given
in parameter
- * `related(rtype, x='subject', limit=None, entities=False)`, returns a list
- of entities related to the current entity by the relation given in parameter
+ * `related(rtype, role='subject', limit=None, entities=False)`,
+ returns a list of entities related to the current entity by the
+ relation given in parameter
- * `unrelated(rtype, targettype, x='subject', limit=None)`, returns a result set
- corresponding to the entities not related to the current entity by the
- relation given in parameter and satisfying its constraints
+ * `unrelated(rtype, targettype, role='subject', limit=None)`,
+ returns a result set corresponding to the entities not (yet)
+ related to the current entity by the relation given in parameter
+ and satisfying its constraints
* `set_attributes(**kwargs)`, updates the attributes list with the corresponding
values given named parameters
+ * `set_relations(**kwargs)`, add relations to the given object. To
+ set a relation where this entity is the object of the relation,
+ use 'reverse_'<relation> as argument name. Values may be an
+ entity, a list of entities, or None (meaning that all relations of
+ the given type from or to this object should be deleted).
+
* `copy_relations(ceid)`, copies the relations of the entities having the eid
given in the parameters on the current entity
@@ -66,8 +75,10 @@
and helps specializing (by further subclassing) the handling of a
given entity type.
-The methods defined for `AnyEntity`, in addition to `Entity`, are the
-following ones:
+Most methods defined for `AnyEntity`, in addition to `Entity`, add
+support for the `Dublin Core`_ metadata.
+
+.. _`Dublin Core`: http://dublincore.org/
:Standard meta-data (Dublin Core):
@@ -85,12 +96,26 @@
* `dc_authors()`, returns a unicode string corresponding to the meta-data
`Authors` (owners by default)
+ * `dc_creator()`, returns a unicode string corresponding to the
+ creator of the entity
+
* `dc_date(date_format=None)`, returns a unicode string corresponding to
the meta-data `Date` (update date by default)
* `dc_type(form='')`, returns a string to display the entity type by
specifying the preferred form (`plural` for a plural form)
+ * `dc_language()`, returns the language used by the entity
+
+
+:Misc methods:
+
+ * `after_deletion_path`, return (path, parameters) which should be
+ used as redirect information when this entity is being deleted
+
+ * `pre_web_edit`, callback called by the web editcontroller when an
+ entity will be created/modified, to let a chance to do some entity
+ specific stuff (does nothing by default)
Inheritance
-----------
--- a/doc/book/en/development/entityclasses/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/entityclasses/index.rst Tue Apr 06 19:08:07 2010 +0200
@@ -10,4 +10,4 @@
data-as-objects
load-sort
interfaces
- more
+ application-logic
--- a/doc/book/en/development/entityclasses/interfaces.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/entityclasses/interfaces.rst Tue Apr 06 19:08:07 2010 +0200
@@ -1,7 +1,9 @@
Interfaces
----------
-Same thing as object-oriented programming interfaces.
+This is the same thing as object-oriented programming `interfaces`_.
+
+.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
Definition of an interface is quite trivial. An example from cubicweb
itself (found in cubicweb/interfaces.py):
@@ -17,7 +19,7 @@
"""returns the item's children"""
def children_rql(self):
- """XXX returns RQL to get children"""
+ """returns RQL to get children"""
def iterchildren(self):
"""iterates over the item's children"""
--- a/doc/book/en/development/entityclasses/more.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-Navigation on deletion
-----------------------
-
-XXX after_deletion_path, pre_web_edit
-
-Controlling output url
------------------------
-
-XXX write me
-
-Controling notification references
-----------------------------------
-
-XXX write me
--- a/doc/book/en/development/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/development/index.rst Tue Apr 06 19:08:07 2010 +0200
@@ -11,12 +11,13 @@
:numbered:
cubes/index
+ vreg.rst
datamodel/index
entityclasses/index
devcore/index
devweb/index
devrepo/index
- testing/index
- migration/index
+ testing.rst
+ migration.rst
webstdlib/index
- profiling/index
+ profiling.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/migration.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,198 @@
+.. -*- coding: utf-8 -*-
+
+.. _migration:
+
+Migration
+=========
+
+One of the main design goals of *CubicWeb* was to support iterative and agile
+development. For this purpose, multiple actions are provided to facilitate the
+improvement of an instance, and in particular to handle the changes to be
+applied to the data model, without loosing existing data.
+
+The current version of a cube (and of cubicweb itself) is provided in the file
+`__pkginfo__.py` as a tuple of 3 integers.
+
+Migration scripts management
+----------------------------
+
+Migration scripts has to be located in the directory `migration` of your
+cube and named accordingly:
+
+::
+
+ <version n° X.Y.Z>[_<description>]_<mode>.py
+
+in which :
+
+* X.Y.Z is the model version number to which the script enables to migrate.
+
+* *mode* (between the last "_" and the extension ".py") is used for
+ distributed installation. It indicates to which part
+ of the application (RQL server, web server) the script applies.
+ Its value could be :
+
+ * `common`, applies to the RQL server as well as the web server and updates
+ files on the hard drive (configuration files migration for example).
+
+ * `web`, applies only to the web server and updates files on the hard drive.
+
+ * `repository`, applies only to the RQL server and updates files on the
+ hard drive.
+
+ * `Any`, applies only to the RQL server and updates data in the database
+ (schema and data migration for example).
+
+Again in the directory `migration`, the file `depends.map` allows to indicate
+that for the migration to a particular model version, you always have to first
+migrate to a particular *CubicWeb* version. This file can contain comments (lines
+starting by `#`) and a dependancy is listed as follows: ::
+
+ <model version n° X.Y.Z> : <cubicweb version n° X.Y.Z>
+
+For example: ::
+
+ 0.12.0: 2.26.0
+ 0.13.0: 2.27.0
+ # 0.14 works with 2.27 <= cubicweb <= 2.28 at least
+ 0.15.0: 2.28.0
+
+Base context
+------------
+
+The following identifiers are pre-defined in migration scripts:
+
+* `config`, instance configuration
+
+* `interactive_mode`, boolean indicating that the script is executed in
+ an interactive mode or not
+
+* `versions_map`, dictionary of migrated versions (key are cubes
+ names, including 'cubicweb', values are (from version, to version)
+
+* `confirm(question)`, function asking the user and returning true
+ if the user answers yes, false otherwise (always returns true in
+ non-interactive mode)
+
+* the function `_`, it is equivalent to `unicode` allowing to flag the strings
+ to internationalize in the migration scripts.
+
+In the `repository` scripts, the following identifiers are also defined:
+
+* `checkpoint`, request confirming and executing a "commit" at checking point
+
+* `schema`, instance schema (readen from the database)
+
+* `fsschema`, installed schema on the file system (e.g. schema of
+ the updated model and cubicweb)
+
+* `repo`, repository object
+
+* `session`, repository session object
+
+
+Schema migration
+----------------
+The following functions for schema migration are available in `repository`
+scripts:
+
+* `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new
+ attribute to an existing entity type. If the attribute type is not specified,
+ then it is extracted from the updated schema.
+
+* `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an
+ existing entity type.
+
+* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute
+
+* `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type.
+ If `auto` is True, all the relations using this entity type and having a known
+ entity type on the other hand will automatically be added.
+
+* `drop_entity_type(etype, commit=True)`, removes an entity type and all the
+ relations using it.
+
+* `rename_entity_type(oldname, newname, commit=True)`, renames an entity type
+
+* `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation
+ type. If `addrdef` is True, all the relations definitions of this type will
+ be added.
+
+* `drop_relation_type(rtype, commit=True)`, removes a relation type and all the
+ definitions of this type.
+
+* `rename_relation(oldname, newname, commit=True)`, renames a relation.
+
+* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, adds a new
+ relation definition.
+
+* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes
+ a relation definition.
+
+* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`,
+ synchronizes properties and/or permissions on:
+ - the whole schema if ertype is None
+ - an entity or relation type schema if ertype is a string
+ - a relation definition if ertype is a 3-uple (subject, relation, object)
+
+* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
+ properties of a relation definition by using the named parameters of the properties
+ to change.
+
+* `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the
+ relation <rtype> of entity type <etype>.
+
+* `set_size_constraint(etype, rtype, size, commit=True)`, changes the size constraints
+ for the relation <rtype> of entity type <etype>.
+
+Data migration
+--------------
+The following functions for data migration are available in `repository` scripts:
+
+* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
+ query, either to interrogate or update. A result set object is returned.
+
+* `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given
+ type. The attribute and relation values are specified using the named and
+ positionned parameters.
+
+Workflow creation
+-----------------
+
+The following functions for workflow creation are available in `repository`
+scripts:
+
+* `add_workflow(label, workflowof, initial=False, commit=False, **kwargs)`, adds a new workflow
+ for a given type(s)
+
+You can find more details about workflows in the chapter :ref:`Workflow` .
+
+Configuration migration
+-----------------------
+
+The following functions for configuration migration are available in all
+scripts:
+
+* `option_renamed(oldname, newname)`, indicates that an option has been renamed
+
+* `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not
+ belong anymore to the same group.
+
+* `option_added(oldname, newname)`, indicates that an option has been added.
+
+* `option_removed(oldname, newname)`, indicates that an option has been deleted.
+
+
+Others migration functions
+--------------------------
+Those functions are only used for low level operations that could not be
+accomplished otherwise or to repair damaged databases during interactive
+session. They are available in `repository` scripts:
+
+* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source
+* `add_entity_type_table(etype, commit=True)`
+* `add_relation_type_table(rtype, commit=True)`
+* `uninline_relation(rtype, commit=True)`
+
+
+[FIXME] Add explanation on how to use cubicweb-ctl shell
--- a/doc/book/en/development/migration/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,198 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _migration:
-
-Migration
-=========
-
-One of the main design goals of *CubicWeb* was to support iterative and agile
-development. For this purpose, multiple actions are provided to facilitate the
-improvement of an instance, and in particular to handle the changes to be
-applied to the data model, without loosing existing data.
-
-The current version of a cube (and of cubicweb itself) is provided in the file
-`__pkginfo__.py` as a tuple of 3 integers.
-
-Migration scripts management
-----------------------------
-
-Migration scripts has to be located in the directory `migration` of your
-cube and named accordingly:
-
-::
-
- <version n° X.Y.Z>[_<description>]_<mode>.py
-
-in which :
-
-* X.Y.Z is the model version number to which the script enables to migrate.
-
-* *mode* (between the last "_" and the extension ".py") is used for
- distributed installation. It indicates to which part
- of the application (RQL server, web server) the script applies.
- Its value could be :
-
- * `common`, applies to the RQL server as well as the web server and updates
- files on the hard drive (configuration files migration for example).
-
- * `web`, applies only to the web server and updates files on the hard drive.
-
- * `repository`, applies only to the RQL server and updates files on the
- hard drive.
-
- * `Any`, applies only to the RQL server and updates data in the database
- (schema and data migration for example).
-
-Again in the directory `migration`, the file `depends.map` allows to indicate
-that for the migration to a particular model version, you always have to first
-migrate to a particular *CubicWeb* version. This file can contain comments (lines
-starting by `#`) and a dependancy is listed as follows: ::
-
- <model version n° X.Y.Z> : <cubicweb version n° X.Y.Z>
-
-For example: ::
-
- 0.12.0: 2.26.0
- 0.13.0: 2.27.0
- # 0.14 works with 2.27 <= cubicweb <= 2.28 at least
- 0.15.0: 2.28.0
-
-Base context
-------------
-
-The following identifiers are pre-defined in migration scripts:
-
-* `config`, instance configuration
-
-* `interactive_mode`, boolean indicating that the script is executed in
- an interactive mode or not
-
-* `versions_map`, dictionary of migrated versions (key are cubes
- names, including 'cubicweb', values are (from version, to version)
-
-* `confirm(question)`, function asking the user and returning true
- if the user answers yes, false otherwise (always returns true in
- non-interactive mode)
-
-* the function `_`, it is equivalent to `unicode` allowing to flag the strings
- to internationalize in the migration scripts.
-
-In the `repository` scripts, the following identifiers are also defined:
-
-* `checkpoint`, request confirming and executing a "commit" at checking point
-
-* `schema`, instance schema (readen from the database)
-
-* `fsschema`, installed schema on the file system (e.g. schema of
- the updated model and cubicweb)
-
-* `repo`, repository object
-
-* `session`, repository session object
-
-
-Schema migration
-----------------
-The following functions for schema migration are available in `repository`
-scripts:
-
-* `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new
- attribute to an existing entity type. If the attribute type is not specified,
- then it is extracted from the updated schema.
-
-* `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an
- existing entity type.
-
-* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute
-
-* `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type.
- If `auto` is True, all the relations using this entity type and having a known
- entity type on the other hand will automatically be added.
-
-* `drop_entity_type(etype, commit=True)`, removes an entity type and all the
- relations using it.
-
-* `rename_entity_type(oldname, newname, commit=True)`, renames an entity type
-
-* `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation
- type. If `addrdef` is True, all the relations definitions of this type will
- be added.
-
-* `drop_relation_type(rtype, commit=True)`, removes a relation type and all the
- definitions of this type.
-
-* `rename_relation(oldname, newname, commit=True)`, renames a relation.
-
-* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, adds a new
- relation definition.
-
-* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes
- a relation definition.
-
-* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`,
- synchronizes properties and/or permissions on:
- - the whole schema if ertype is None
- - an entity or relation type schema if ertype is a string
- - a relation definition if ertype is a 3-uple (subject, relation, object)
-
-* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
- properties of a relation definition by using the named parameters of the properties
- to change.
-
-* `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the
- relation <rtype> of entity type <etype>.
-
-* `set_size_constraint(etype, rtype, size, commit=True)`, changes the size constraints
- for the relation <rtype> of entity type <etype>.
-
-Data migration
---------------
-The following functions for data migration are available in `repository` scripts:
-
-* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
- query, either to interrogate or update. A result set object is returned.
-
-* `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given
- type. The attribute and relation values are specified using the named and
- positionned parameters.
-
-Workflow creation
------------------
-
-The following functions for workflow creation are available in `repository`
-scripts:
-
-* `add_workflow(label, workflowof, initial=False, commit=False, **kwargs)`, adds a new workflow
- for a given type(s)
-
-You can find more details about workflows in the chapter :ref:`Workflow` .
-
-Configuration migration
------------------------
-
-The following functions for configuration migration are available in all
-scripts:
-
-* `option_renamed(oldname, newname)`, indicates that an option has been renamed
-
-* `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not
- belong anymore to the same group.
-
-* `option_added(oldname, newname)`, indicates that an option has been added.
-
-* `option_removed(oldname, newname)`, indicates that an option has been deleted.
-
-
-Others migration functions
---------------------------
-Those functions are only used for low level operations that could not be
-accomplished otherwise or to repair damaged databases during interactive
-session. They are available in `repository` scripts:
-
-* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source
-* `add_entity_type_table(etype, commit=True)`
-* `add_relation_type_table(rtype, commit=True)`
-* `uninline_relation(rtype, commit=True)`
-
-
-[FIXME] Add explanation on how to use cubicweb-ctl shell
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/profiling.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,55 @@
+Profiling and performance
+=========================
+
+If you feel that one of your pages takes more time than it should to be
+generated, chances are that you're making too many RQL queries. Obviously,
+there are other reasons but experience tends to show this is the first thing to
+track down. Luckily, CubicWeb provides a configuration option to log RQL
+queries. In your ``all-in-one.conf`` file, set the **query-log-file** option::
+
+ # web application query log file
+ query-log-file=~/myapp-rql.log
+
+Then restart your application, reload your page and stop your application.
+The file ``myapp-rql.log`` now contains the list of RQL queries that were
+executed during your test. It's a simple text file containing lines such as::
+
+ Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec)
+ Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec)
+
+The structure of each line is::
+
+ <RQL QUERY> <QUERY ARGS IF ANY> -- <TIME SPENT>
+
+CubicWeb also provides the **exlog** command to examine and summarize data found
+in such a file:
+
+.. sourcecode:: sh
+
+ $ cubicweb-ctl exlog < ~/myapp-rql.log
+ 0.07 50 Any A WHERE X eid %(x)s, X firstname A {}
+ 0.05 50 Any A WHERE X eid %(x)s, X lastname A {}
+ 0.01 1 Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E employees X, X modification_date AA {}
+ 0.01 1 Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s {, }
+ 0.01 1 Any B,T,P ORDERBY lower(T) WHERE B is Bookmark,B title T, B path P, B bookmarked_by U, U eid %(x)s {}
+ 0.01 1 Any A,B,C,D WHERE A eid %(x)s,A name B,A creation_date C,A modification_date D {}
+
+This command sorts and uniquifies queries so that it's easy to see where
+is the hot spot that needs optimization.
+
+Do not neglect to set the **fetch_attrs** attribute you can define in your
+entity classes because it can greatly reduce the number of queries executed (see
+:ref:`FetchAttrs`).
+
+You should also know about the **profile** option in the ``all-in-on.conf``. If
+set, this option will make your application run in an `hotshot`_ session and
+store the results in the specified file.
+
+.. _hotshot: http://docs.python.org/library/hotshot.html#module-hotshot
+
+Last but no least, if you're using the PostgreSQL database backend, VACUUMing
+your database can significantly improve the performance of the queries (by
+updating the statistics used by the query optimizer). Nowadays, this is done
+automatically from time to time, but if you've just imported a large amount of
+data in your db, you will want to vacuum it (with the analyse option on). Read
+the documentation of your database for more information.
--- a/doc/book/en/development/profiling/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-Profiling and performance
-=========================
-
-If you feel that one of your pages takes more time than it should to be
-generated, chances are that you're making too many RQL queries. Obviously,
-there are other reasons but experience tends to show this is the first thing to
-track down. Luckily, CubicWeb provides a configuration option to log RQL
-queries. In your ``all-in-one.conf`` file, set the **query-log-file** option::
-
- # web application query log file
- query-log-file=~/myapp-rql.log
-
-Then restart your application, reload your page and stop your application.
-The file ``myapp-rql.log`` now contains the list of RQL queries that were
-executed during your test. It's a simple text file containing lines such as::
-
- Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec)
- Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec)
-
-The structure of each line is::
-
- <RQL QUERY> <QUERY ARGS IF ANY> -- <TIME SPENT>
-
-CubicWeb also provides the **exlog** command to examine and summarize data found
-in such a file:
-
-.. sourcecode:: sh
-
- $ cubicweb-ctl exlog < ~/myapp-rql.log
- 0.07 50 Any A WHERE X eid %(x)s, X firstname A {}
- 0.05 50 Any A WHERE X eid %(x)s, X lastname A {}
- 0.01 1 Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E employees X, X modification_date AA {}
- 0.01 1 Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s {, }
- 0.01 1 Any B,T,P ORDERBY lower(T) WHERE B is Bookmark,B title T, B path P, B bookmarked_by U, U eid %(x)s {}
- 0.01 1 Any A,B,C,D WHERE A eid %(x)s,A name B,A creation_date C,A modification_date D {}
-
-This command sorts and uniquifies queries so that it's easy to see where
-is the hot spot that needs optimization.
-
-Do not neglect to set the **fetch_attrs** attribute you can define in your
-entity classes because it can greatly reduce the number of queries executed (see
-:ref:`FetchAttrs`).
-
-You should also know about the **profile** option in the ``all-in-on.conf``. If
-set, this option will make your application run in an `hotshot`_ session and
-store the results in the specified file.
-
-.. _hotshot: http://docs.python.org/library/hotshot.html#module-hotshot
-
-Last but no least, if you're using the PostgreSQL database backend, VACUUMing
-your database can significantly improve the performance of the queries (by
-updating the statistics used by the query optimizer). Nowadays, this is done
-automatically from time to time, but if you've just imported a large amount of
-data in your db, you will want to vacuum it (with the analyse option on). Read
-the documentation of your database for more information.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/testing.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,78 @@
+.. -*- coding: utf-8 -*-
+
+Tests
+=====
+
+.. toctree::
+ :maxdepth: 1
+
+
+Unit tests
+----------
+
+*CubicWeb* framework provides essentially two Python test classes in the
+module `cubicweb.devtools.apptest`:
+
+* `EnvBasedTC`, to simulate a complete environment (web + repository)
+* `RepositoryBasedTC`, to simulate a repository environment only
+
+Those two classes almost have the same interface and offer numerous
+methods to write tests rapidly and efficiently.
+
+XXX FILLME describe API
+
+In most of the cases, you will inherit `EnvBasedTC` to write Unittest or
+functional tests for your entities, views, hooks, etc...
+
+Managing connections or users
++++++++++++++++++++++++++++++
+
+Since unit tests are done with the SQLITE backend and this does not
+support multiple connections at a time, you must be careful when
+simulating security, changing users.
+
+By default, tests run with a user with admin privileges. This
+user/connection must never be closed.
+qwq
+Before a self.login, one has to release the connection pool in use with a self.commit, self.rollback or self.close.
+
+When one is logged in as a normal user and wants to switch back to the admin user, one has to use self.restore_connection().
+
+Usually it looks like this:
+
+.. sourcecode:: python
+
+ # execute using default admin connection
+ self.execute(...)
+ # I want to login with another user, ensure to free admin connection pool
+ # (could have used rollback but not close here, we should never close defaut admin connection)
+ self.commit()
+ cnx = self.login('user')
+ # execute using user connection
+ self.execute(...)
+ # I want to login with another user or with admin user
+ self.commit(); cnx.close()
+ # restore admin connection, never use cnx = self.login('admin'), it will return
+ # the default admin connection and one may be tempted to close it
+ self.restore_connection()
+
+Do not use the references kept to the entities created with a connection from another.
+
+
+Email notifications tests
+-------------------------
+When running tests potentially generated e-mails are not really
+sent but is found in the list `MAILBOX` of module `cubicweb.devtools.apptest`.
+This list is reset at each test *setUp* (by the setUp of classes `EnvBasedTC`
+and `RepositoryBasedTC`).
+
+
+You can test your notifications by analyzing the contents of this list, which
+contains objects with two attributes:
+* `recipients`, the list of recipients
+* `msg`, object email.Message
+
+
+Automatic testing
+-----------------
+XXXFILLME
--- a/doc/book/en/development/testing/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-Tests
-=====
-
-.. toctree::
- :maxdepth: 1
-
-
-Unit tests
-----------
-
-*CubicWeb* framework provides essentially two Python test classes in the
-module `cubicweb.devtools.apptest`:
-
-* `EnvBasedTC`, to simulate a complete environment (web + repository)
-* `RepositoryBasedTC`, to simulate a repository environment only
-
-Those two classes almost have the same interface and offer numerous
-methods to write tests rapidly and efficiently.
-
-XXX FILLME describe API
-
-In most of the cases, you will inherit `EnvBasedTC` to write Unittest or
-functional tests for your entities, views, hooks, etc...
-
-Managing connections or users
-+++++++++++++++++++++++++++++
-
-Since unit tests are done with the SQLITE backend and this does not
-support multiple connections at a time, you must be careful when
-simulating security, changing users.
-
-By default, tests run with a user with admin privileges. This
-user/connection must never be closed.
-qwq
-Before a self.login, one has to release the connection pool in use with a self.commit, self.rollback or self.close.
-
-When one is logged in as a normal user and wants to switch back to the admin user, one has to use self.restore_connection().
-
-Usually it looks like this:
-
-.. sourcecode:: python
-
- # execute using default admin connection
- self.execute(...)
- # I want to login with another user, ensure to free admin connection pool
- # (could have used rollback but not close here, we should never close defaut admin connection)
- self.commit()
- cnx = self.login('user')
- # execute using user connection
- self.execute(...)
- # I want to login with another user or with admin user
- self.commit(); cnx.close()
- # restore admin connection, never use cnx = self.login('admin'), it will return
- # the default admin connection and one may be tempted to close it
- self.restore_connection()
-
-Do not use the references kept to the entities created with a connection from another.
-
-
-Email notifications tests
--------------------------
-When running tests potentially generated e-mails are not really
-sent but is found in the list `MAILBOX` of module `cubicweb.devtools.apptest`.
-This list is reset at each test *setUp* (by the setUp of classes `EnvBasedTC`
-and `RepositoryBasedTC`).
-
-
-You can test your notifications by analyzing the contents of this list, which
-contains objects with two attributes:
-* `recipients`, the list of recipients
-* `msg`, object email.Message
-
-
-Automatic testing
------------------
-XXXFILLME
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/vreg.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,110 @@
+The VRegistry, selectors and application objects
+================================================
+
+This chapter talk about core concepts of the |cubicweb| framework, that make it
+different from other framework (and probably not easy to grasp at a first
+glance). You won't be able to do advanced development with |cubicweb| without
+a good understanding of what's explain below.
+
+This chapter goes deep into details. You don't have to remember them all but keep
+it in mind so you can go back there later...
+
+.. toctree::
+ :maxdepth: 1
+
+.. autodocstring:: cubicweb.cwvreg
+.. autodocstring:: cubicweb.selectors
+.. automodule:: cubicweb.appobject
+
+Base selectors
+--------------
+
+Selectors are scoring functions that are called by the registry to tell whenever
+an appobject can be selected in a given context. Selector sets are for instance
+the glue that tie views to the data model. Using them appropriately is an
+essential part of the construction of well behaved cubes.
+
+Of course you may have to write your own set of selectors as your needs grows and
+you get familiar with the framework (see :ref:`CustomSelectors`).
+
+Here is a description of generic selectors provided by CubicWeb that should suit
+most of your needs.
+
+Bare selectors
+~~~~~~~~~~~~~~
+Those selectors are somewhat dumb, which doesn't mean they're not (very) useful.
+
+.. autoclass:: cubicweb.appobject.yes
+.. autoclass:: cubicweb.selectors.match_kwargs
+.. autoclass:: cubicweb.selectors.appobject_selectable
+
+
+Result set selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for a result set in the context ('rset' argument or
+the input context) and match or not according to its shape. Some of these
+selectors have different behaviour if a particular cell of the result set is
+specified using 'row' and 'col' arguments of the input context or not.
+
+.. autoclass:: cubicweb.selectors.none_rset
+.. autoclass:: cubicweb.selectors.any_rset
+.. autoclass:: cubicweb.selectors.nonempty_rset
+.. autoclass:: cubicweb.selectors.empty_rset
+.. autoclass:: cubicweb.selectors.one_line_rset
+.. autoclass:: cubicweb.selectors.multi_lines_rset
+.. autoclass:: cubicweb.selectors.multi_columns_rset
+.. autoclass:: cubicweb.selectors.paginated_rset
+.. autoclass:: cubicweb.selectors.sorted_rset
+.. autoclass:: cubicweb.selectors.one_etype_rset
+.. autoclass:: cubicweb.selectors.multi_etypes_rset
+
+
+Entity selectors
+~~~~~~~~~~~~~~~~
+Those selectors are looking for either an `entity` argument in the input context,
+or entity found in the result set ('rset' argument or the input context) and
+match or not according to entity's (instance or class) properties.
+
+.. autoclass:: cubicweb.selectors.non_final_entity
+.. autoclass:: cubicweb.selectors.implements
+.. autoclass:: cubicweb.selectors.score_entity
+.. autoclass:: cubicweb.selectors.rql_condition
+.. autoclass:: cubicweb.selectors.relation_possible
+.. autoclass:: cubicweb.selectors.partial_relation_possible
+.. autoclass:: cubicweb.selectors.has_related_entities
+.. autoclass:: cubicweb.selectors.partial_has_related_entities
+.. autoclass:: cubicweb.selectors.has_permission
+.. autoclass:: cubicweb.selectors.has_add_permission
+
+
+Logged user selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for properties of the user issuing the request.
+
+.. autoclass:: cubicweb.selectors.anonymous_user
+.. autoclass:: cubicweb.selectors.authenticated_user
+.. autoclass:: cubicweb.selectors.match_user_groups
+
+
+Web request selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for properties of *web* request, they can not be
+used on the data repository side.
+
+.. autoclass:: cubicweb.selectors.match_form_params
+.. autoclass:: cubicweb.selectors.match_search_state
+.. autoclass:: cubicweb.selectors.match_context_prop
+.. autoclass:: cubicweb.selectors.match_view
+.. autoclass:: cubicweb.selectors.primary_view
+.. autoclass:: cubicweb.selectors.specified_etype_implements
+
+
+Other selectors
+~~~~~~~~~~~~~~~
+.. autoclass:: cubicweb.selectors.match_transition
+
+You'll also find some other (very) specific selectors hidden in other modules
+than :mod:`cubicweb.selectors`.
+
+
+.. |cubicweb| replace:: *CubicWeb*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/intro/concepts.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,343 @@
+.. -*- coding: utf-8 -*-
+
+.. _Concepts:
+
+The Core Concepts of |cubicweb|
+===============================
+
+This section defines some terms and core concepts of the |cubicweb| framework. To
+avoid confusion while reading this book, take time to go through the following
+definitions and use this section as a reference during your reading.
+
+
+.. _Cube:
+
+Cubes
+-----
+
+A cube is a software component made of three parts: its data model
+(:file:`schema`), its logic (:file:`entities`) and its user interface
+(:file:`views`).
+
+A cube can use other cubes as building blocks and assemble them to provide a
+whole with richer functionnalities than its parts. The cubes `cubicweb-blog`_ and
+`cubicweb-comment`_ could be used to make a cube named *myblog* with commentable
+blog entries.
+
+The `CubicWeb.org Forge`_ offers a large number of cubes developed by the community
+and available under a free software license.
+
+The command :command:`cubicweb-ctl list` displays the list of cubes installed on
+your system.
+
+On a Unix system, the available cubes are usually stored in the directory
+:file:`/usr/share/cubicweb/cubes`. If you're using the cubicweb forest
+(:ref:SourceInstallation), the cubes are searched in the directory
+:file:`/path/to/cubicweb_forest/cubes`. The environment variable
+:envvar:`CW_CUBES_PATH` gives additionnal locations where to search for cubes.
+
+.. _`CubicWeb.org Forge`: http://www.cubicweb.org/project/
+.. _`cubicweb-blog`: http://www.cubicweb.org/project/cubicweb-blog
+.. _`cubicweb-comment`: http://www.cubicweb.org/project/cubicweb-comment
+
+
+.. _Instance:
+
+Instances
+---------
+
+An instance is a runnable application installed on a computer and based on a
+cube.
+
+The instance directory contains the configuration files. Several instances can be
+created and based on the same cube. For exemple, several software forges can be
+set up on one computer system based on the `cubicweb-forge`_ cube.
+
+.. _`cubicweb-forge`: http://www.cubicweb.org/project/cubicweb-forge
+
+Instances can be of three different types: all-in-one, web engine or data
+repository. For applications that support high traffic, several web (front-end)
+and data (back-end) instances can be set-up to share the load.
+
+.. image:: ../../images/archi_globale.en.png
+
+The command :command:`cubicweb-ctl list` also displays the list of instances
+installed on your system.
+
+On a Unix system, the instances are usually stored in the directory
+:file:`/etc/cubicweb.d/`. During development, the :file:`~/etc/cubicweb.d/`
+directory is looked up, as well as the paths in :envvar:`CW_INSTANCES_DIR`
+environment variable.
+
+
+.. Note::
+
+ The term application is used to refer to "something that should do something as
+ a whole", eg more like a project and so can refer to an instance or to a cube,
+ depending on the context. This book will try to use *application*, *cube* and
+ *instance* as appropriate.
+
+
+.. _RepositoryIntro:
+
+Data Repository
+---------------
+
+The data repository [1]_ provides access to one or more data sources (including
+SQL databases, LDAP repositories, other |cubicweb| instance repositories, GAE's
+DataStore, etc).
+
+All interactions with the repository are done using the Relation Query Language
+(:ref:`RQL`). The repository federates the data sources and hides them from the
+querier, which does not realize when a query spans accross several data sources
+and requires running sub-queries and merges to complete.
+
+It is common to run the web engine and the repository in the same process (see
+instances of type all-in-one above), but this is not a requirement. A repository
+can be set up to be accessed remotely using Pyro (`Python Remote Objects`_) and
+act as a server. However, it's important to know if code you're writing is
+executed on the repository side, on our client side (the web engine being a
+client for instance): you don't have the same abilities on both side. On the
+repository side, you can for instance by-pass security checks, which isn't
+possible from client code.
+
+Some logic can be attached to events that happen in the repository, like
+creation of entities, deletion of relations, etc. This is used for example to
+send email notifications when the state of an object changes. See :ref:`HookIntro` below.
+
+.. [1] not to be confused with a Mercurial repository or a Debian repository.
+.. _`Python Remote Objects`: http://pyro.sourceforge.net/
+
+
+.. _WebEngineIntro:
+
+Web Engine
+----------
+
+The web engine replies to http requests and runs the user interface
+and most of the application logic.
+
+By default the web engine provides a `CRUD`_ user interface based on
+the data model of the instance. Entities can be created, displayed,
+updated and deleted. As the default user interface is not very fancy,
+it is usually necessary to develop your own.
+
+.. _`CRUD`: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
+
+.. _SchemaIntro:
+
+Schema (Data Model)
+-------------------
+
+The data model of a cube is described as an entity-relationship schema using a
+comprehensive language made of Python classes imported from the yams_ library.
+
+.. _yams: http://www.logilab.org/project/yams/
+
+An `entity type` defines a set of attributes and is used in some relations.
+Attributes may be of the following types: `String`, `Int`, `Float`, `Boolean`,
+`Date`, `Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`.
+
+A `relation type` is used to define an oriented binary relation between two
+entity types. The left-hand part of a relation is named the `subject` and the
+right-hand part is named the `object`.
+
+A `relation definition` is a triple (*subject entity type*, *relation type*, *object
+entity type*) associated with a set of properties such as cardinality,
+constraints, etc.
+
+Permissions can be set on entity types and relation definition to control who
+will be able to create, read, update or delete entities and relations. Permissions
+are granted to groups (to which users may belong) or using rql expression (if the
+rql expression returns some results, the permission is granted).
+
+Some meta-data necessary to the system is added to the data model. That includes
+entities like users and groups, the entities used to store the data model
+itself and attributes like unique identifier, creation date, creator, etc.
+
+When you create a new |cubicweb| instance, the schema is stored in the database.
+When the cubes the instance is based on evolve, they may change their data model
+and provide migration scripts that will be executed when the administrator will
+run the upgrade process for the instance.
+
+
+.. _VRegistryIntro:
+
+Registries and application objects
+----------------------------------
+
+Application objects
+~~~~~~~~~~~~~~~~~~~
+
+Beside a few core functionalities, almost every feature of the framework is
+achieved by dynamic objects (`application objects` or `appobjects`) stored in a
+two-levels registry (the `vregistry`). Each object is affected to a registry with
+an identifier in this registry. You may have more than one object sharing an
+identifier in the same registry, At runtime, appobjects are selected in a
+registry according to the context. Selection is done by comparing *score*
+returned by each appobject's *selector*.
+
+Application objects are stored in the vregistry using a two-level hierarchy :
+
+ object's `__registry__` : object's `__regid__` : [list of app objects]
+
+E.g. The `vregistry` contains several registries which hold a list of
+appobjects associated to an identifier.
+
+The base class of appobjects is :class:`cubicweb.appobject.AppObject`.
+
+Selectors
+~~~~~~~~~
+
+Each appobject has a selector, that is used to compute how well the object fits a
+given context. The better the object fits the context, the higher the score. They
+are the glue that tie appobjects to the data model. Using them appropriately is
+an essential part of the construction of well behaved cubes.
+
+|cubicweb| provides a set of basic selectors that may be parametrized. Also,
+selectors can be combined with the `~` unary operator (negation) and the binary
+operators `&` and `|` (respectivly 'and' and 'or') to build more complex
+selector. Of course complex selector may be combined too. Last but not least, you
+can write your own selectors.
+
+The `vregistry`
+~~~~~~~~~~~~~~~
+
+At startup, the `vregistry` inspects a number of directories looking for
+compatible classes definition. After a recording process, the objects are
+assigned to registries so that they can be selected dynamically while the
+instance is running.
+
+In a cube, application object classes are looked in the following modules or
+packages:
+
+- `entities`
+- `views`
+- `sobjects`
+- `hooks`
+
+
+Once initialized, there are three common ways to retrieve some application object
+from a registry:
+
+* get the most appropriate object by specifying an identifier. In that case, the
+ object with the greatest score is selected. There should always be a single
+ appobject with a greater score than others for a particular context.
+
+* get all objects applying to a context by specifying a registry. In that case, a
+ list of objects will be returned containing the object with the highest score
+ (> 0) for each identifier in that registry.
+
+* get the object within a particular registry/identifier. In that case no
+ selection process is involved, the vregistry will expect to find a single
+ object in that cell.
+
+
+.. _RQLIntro:
+
+The RQL query language
+----------------------
+
+**No need for a complicated ORM when you have a powerful query language**
+
+All the persistent data in a |cubicweb| instance is retrieved and modified by
+using the Relation Query Language.
+
+This query language is inspired by SQL but is on a higher level in order to
+emphasize browsing relations.
+
+
+db-api
+~~~~~~
+
+The repository exposes a `db-api`_ like api but using the RQL instead of SQL.
+
+You basically get a connection using :func:`cubicweb.dbapi.connect` , then
+get a cursor to call its `execute` method which will return result set for the
+given rql query.
+
+You can also get additional information through the connection, such as the
+repository'schema, version configuration, etc.
+
+
+Result set
+~~~~~~~~~~
+
+Every request made (using RQL) to the data repository returns an object we call a
+Result Set. It enables easy use of the retrieved data, providing a translation
+layer between the backend's native datatypes and |cubicweb| schema's EntityTypes.
+
+Result sets provide access to the raw data, yielding either basic Python data
+types, or schema-defined high-level entities, in a straightforward way.
+
+
+.. _ViewIntro:
+
+Views
+-----
+
+**CubicWeb is data driven**
+
+The view system is loosely coupled to data through the selection system explained
+above. Views are application objects with a dedicated interface to 'render'
+something, eg producing some html, text, xml, pdf, or whatsover that can be
+displayed to a user.
+
+The two main entry points of a view are:
+
+* `call()`, used to render a view on a context with no result set, or on a whole
+ result set
+
+* `cell_call(row, col)`, used to render a view on a the cell with index `row` and
+ `col` of the context's result set (remember result set may be seen as a two
+ dimensions array).
+
+Then view may gets refined into different kind of objects such as `template`,
+`boxes`, `components`, which are more high-level abstraction useful to build
+the user interface in an object oriented way.
+
+
+.. _HookIntro:
+
+Hooks and operations
+--------------------
+
+**CubicWeb provides an extensible data repository**
+
+The data model defined using Yams types allows to express the data
+model in a comfortable way. However several aspects of the data model
+can not be expressed there. For instance:
+
+* managing computed attributes
+
+* enforcing complicated structural invariants
+
+* real-world side-effects linked to data events (email notification
+ being a prime example)
+
+The hook system is much like the triggers of an SQL database engine,
+except that:
+
+* it is not limited to one specific SQL backend (every one of them
+ having an idiomatic way to encode triggers), nor to SQL backends at
+ all (think about LDAP or a Subversion repository)
+
+* it is well-coupled to the rest of the framework
+
+Hooks are also application objects registered on events such as after/before
+add/update/delete on entities/relations, server startup or shutdown, etc. As all
+application objects, they have a selector defining when they should be called or
+not.
+
+`Operations` may be instantiated by hooks to do further processing at different
+steps of the transaction's commit / rollback, which usually can not be done
+safely at the hook execution time.
+
+Hooks and operation are an essential building block of any moderately complicated
+cubicweb application.
+
+.. Note:
+ RQL queries executed in hooks and operations are *unsafe* by default, e.g. the
+ read and write security is deactivated unless explicitly asked.
+
+.. |cubicweb| replace:: *CubicWeb*
--- a/doc/book/en/intro/concepts/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,317 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _Concepts:
-
-The Core Concepts of |cubicweb|
-===============================
-
-This section defines some terms and core concepts of the |cubicweb|
-framework. To avoid confusion while reading this book, take time to go through
-the following definitions and use this section as a reference during your
-reading.
-
-.. _Cube:
-
-Cubes
------
-
-A cube is a software component made of three parts: its data model
-(:file:`schema`), its logic (:file:`entities`) and its user interface
-(:file:`views`).
-
-A cube can use other cubes as building blocks and assemble them to provide
-a whole with richer functionnalities than its parts. The cubes `cubicweb-blog`_
-and `cubicweb-comment`_ could be used to make a cube named *myblog* with
-commentable blog entries.
-
-The `|cubicweb| Forge`_ offers a large number of cubes developed by the community
-and available under a free software license.
-
-The command :command:`cubicweb-ctl list` displays the list of cubes installed on your
-system.
-
-On a Unix system, the available cubes are usually stored in the directory
-:file:`/usr/share/cubicweb/cubes`. If you're using the cubicweb forest
-(:ref:SourceInstallation), the cubes are searched in the directory
-:file:`/path/to/cubicweb_forest/cubes`. The environment variable
-:envvar:`CW_CUBES_PATH` gives additionnal locations where to search for cubes.
-
-.. _`|cubicweb| Forge`: http://www.cubicweb.org/project/
-.. _`cubicweb-blog`: http://www.cubicweb.org/project/cubicweb-blog
-.. _`cubicweb-comment`: http://www.cubicweb.org/project/cubicweb-comment
-
-
-Instances
----------
-
-An instance is a runnable application installed on a computer and based on a
-cube.
-
-The instance directory contains the configuration files. Several instances can
-be created and based on the same cube. For exemple, several software forges can
-be set up on one computer system based on the `cubicweb-forge`_ cube.
-
-.. _`cubicweb-forge`: http://www.cubicweb.org/project/cubicweb-forge
-
-Instances can be of three different types: all-in-one, web engine or data
-repository. For applications that support high traffic, several web (front-end)
-and data (back-end) instances can be set-up to share the load.
-
-.. image:: ../../images/archi_globale.en.png
-
-The command :command:`cubicweb-ctl list` also displays the list of instances
-installed on your system.
-
-On a Unix system, the instances are usually stored in the directory
-:file:`/etc/cubicweb.d/`. During development, the :file:`~/etc/cubicweb.d/`
-directory is looked up, as well as the paths in :envvar:`CW_INSTANCES_DIR`
-environment variable.
-
-The term application is used to refer to "something that should do something as a
-whole", eg more like a project and so can refer to an instance or to a cube,
-depending on the context. This book will try to use *application*, *cube* and
-*instance* as appropriate.
-
-Data Repository
----------------
-
-The data repository [1]_ provides access to one or more data sources (including
-SQL databases, LDAP repositories, Mercurial or Subversion version control
-systems, other |cubicweb| instance repositories, GAE's DataStore, etc).
-
-All interactions with the repository are done using the Relation Query Language
-(RQL). The repository federates the data sources and hides them from the
-querier, which does not realize when a query spans accross several data sources
-and requires running sub-queries and merges to complete.
-
-It is common to run the web engine and the repository in the same process (see
-instances of type all-in-one above), but this is not a requirement. A repository
-can be set up to be accessed remotely using Pyro (`Python Remote Objects`_) and
-act as a server.
-
-Some logic can be attached to events that happen in the repository, like
-creation of entities, deletion of relations, etc. This is used for example to
-send email notifications when the state of an object changes. See `Hooks` below.
-
-.. [1] not to be confused with a Mercurial repository or a Debian repository.
-.. _`Python Remote Objects`: http://pyro.sourceforge.net/
-
-Web Engine
-----------
-
-The web engine replies to http requests and runs the user interface
-and most of the application logic.
-
-By default the web engine provides a default user interface based on
-the data model of the instance. Entities can be created, displayed,
-updated and deleted. As the default user interface is not very fancy,
-it is usually necessary to develop your own.
-
-Schema (Data Model)
--------------------
-
-The data model of a cube is described as an entity-relationship schema using a
-comprehensive language made of Python classes imported from the yams_ library.
-
-.. _yams: http://www.logilab.org/project/yams/
-
-An `entity type` defines a set of attributes and is used in some relations.
-Attributes may be of the following types: `String`, `Int`, `Float`, `Boolean`,
-`Date`, `Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`. See
-:ref:`yams.BASE_TYPES` for details.
-
-A `relation type` is used to define an oriented binary relation between two
-entity types. The left-hand part of a relation is named the `subject` and the
-right-hand part is named the `object`.
-
-A `relation definition` is a triple (*subject entity type*, *relation type*, *object
-entity type*) associated with a set of properties such as cardinality,
-constraints, etc.
-
-Permissions can be set on entity types and relation types to control who will be
-able to create, read, update or delete entities and relations.
-
-Some meta-data necessary to the system is added to the data model. That includes
-entities like users and groups, the entities used to store the data model
-itself and attributes like unique identifier, creation date, creator, etc.
-
-When you create a new |cubicweb| instance, the schema is stored in the database.
-When the cubes the instance is based on evolve, they may change their data model
-and provide migration scripts that will be executed when the administrator will
-run the upgrade process for the instance.
-
-Registries and Objects
-----------------------
-
-Application objects
-~~~~~~~~~~~~~~~~~~~
-
-Beside a few core functionalities, almost every feature of the framework is
-achieved by dynamic objects (`application objects` or `appobjects`) stored in a
-two-levels registry (the `vregistry`). Each object is affected to a registry with
-an identifier in this registry. You may have more than one object sharing an
-identifier in the same registry, At runtime, appobjects are selected in the
-vregistry according to the context.
-
-Application objects are stored in the registry using a two-level hierarchy :
-
- object's `__registry__` : object's `id` : [list of app objects]
-
-The base class of appobjects is `AppObject` (module `cubicweb.appobject`).
-
-The `vregistry`
-~~~~~~~~~~~~~~~
-
-At startup, the `registry` inspects a number of directories looking
-for compatible classes definition. After a recording process, the
-objects are assigned to registers so that they can be selected
-dynamically while the instance is running.
-
-Selectors
-~~~~~~~~~
-
-Each appobject has a selector, that is used to compute how well the object fits
-a given context. The better the object fits the context, the higher the score.
-
-|cubicweb| provides a set of basic selectors that may be parametrized. Selectors
-can be combined with the binary operators `&` and `|` to build more complex
-selector that can be combined too.
-
-There are three common ways to retrieve some appobject from the repository:
-
-* get the most appropriate objects by specifying a registry and an identifier. In
- that case, the object with the greatest score is selected. There should always
- be a single appobject with a greater score than others.
-
-* get all appobjects applying to a context by specifying a registry. In
- that case, every object with the a postive score is selected.
-
-* get the object within a particular registry/identifier. In that case no
- selection process is involved, the vregistry will expect to find a single
- object in that cell.
-
-Selector sets are the glue that tie views to the data model. Using them
-appropriately is an essential part of the construction of well behaved cubes.
-
-When no score is higher than the others, an exception is raised in development
-mode to let you know that the engine was not able to identify the view to
-apply. This error is silenced in production mode and one of the objects with the
-higher score is picked.
-
-If no object has a positive score, ``NoSelectableObject`` exception is raised.
-
-If no object is found for a particular registry and identifier,
-``ObjectNotFound`` exception is raised.
-
-In such cases you would need to review your design and make sure your views are
-properly defined.
-
-
-
-The RQL query language
-----------------------
-
-**No need for a complicated ORM when you have a powerful query language**
-
-All the persistent data in a |cubicweb| instance is retrieved and modified by using the
-Relation Query Language.
-
-This query language is inspired by SQL but is on a higher level in order to
-emphasize browsing relations.
-
-db-api
-~~~~~~
-
-The repository exposes a `db-api`_ like api but using the RQL instead of SQL.
-XXX feed me
-
-Result set
-~~~~~~~~~~
-
-Every request made (using RQL) to the data repository returns an
-object we call a Result Set. It enables easy use of the retrieved
-data, providing a translation layer between the backend's native
-datatypes and |cubicweb| schema's EntityTypes.
-
-Result sets provide access to the raw data, yielding either basic
-Python data types, or schema-defined high-level entities, in a
-straightforward way.
-
-
-Views
------
-
-**CubicWeb| is data driven**
-
-The view system is loosely coupled to data through a selection
-system. Views are, in essence, defined by an id, a selection predicate
-and an entry point (generaly producing html).
-
-XXX feed me.
-
-
-Hooks
------
-
-**CubicWeb provides an extensible data repository**
-
-The data model defined using Yams types allows to express the data
-model in a comfortable way. However several aspects of the data model
-can not be expressed there. For instance:
-
-* managing computed attributes
-
-* enforcing complicated structural invariants
-
-* real-world side-effects linked to data events (email notification
- being a prime example)
-
-The hook system is much like the triggers of an SQL database engine,
-except that:
-
-* it is not limited to one specific SQL backend (every one of them
- having an idiomatic way to encode triggers), nor to SQL backends at
- all (think about LDAP or a Subversion repository)
-
-* it is well-coupled to the rest of the framework
-
-Hooks are basically functions that dispatch on both:
-
-* events : after/before add/update/delete on entities/relations
-
-* entity or relation types
-
-They are an essential building block of any moderately complicated
-cubicweb application.
-
-
-.. _RunMode:
-
-Running mode
-------------
-
-A running mode is a predifined set of configuration telling where it should look
-for various resources, such as cubes, instances, etc. To ease development with
-the framework, there are two running modes with |cubicweb|:
-
-* 'user', resources are searched / created in the user home directory:
- - instances are stored in :file:`~/etc/cubicweb.d`
- - temporary files (such as pid file) in :file:`/tmp`
-
-* 'system', resources are searched / created in the system directories (eg usually requiring root access):
- - instances are stored in :file:`/etc/cubicweb.d`
- - temporary files (such as pid file) in :file:`/var/run/cubicweb`
-
-Cubes search path is also affected, see the :ref:Cube section.
-
-By default, the mode automatically set to 'user' if a :file:`.hg` directory is found
-in the cubicweb package, else it's set to 'system'. You can force this by setting
-the :envvar:`CW_MODE` environment variable to either 'user' or 'system'.
-
-If you've a doubt about the mode you're currently running, check the first line
-outputed by the :command:`cubicweb-ctl list` command.
-
-Notice that each resource path may be explicitly set using an environment
-variable if the default doesn't suit your needs.
-
-.. |cubicweb| replace:: *CubicWeb*
\ No newline at end of file
--- a/doc/book/en/intro/history.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/intro/history.rst Tue Apr 06 19:08:07 2010 +0200
@@ -19,10 +19,10 @@
*CubicWeb* has been developed by Logilab_ and used in-house for many years
before it was first installed for its clients in 2006 as version 2.
-In 2008, *CubicWeb* version 3 became downloadable for free under the terms of
-the LGPL license. Its community is now steadily growing as changes can occur
-rapidly thanks to the time and energy originally put in the design of the
-framework.
+In 2008, *CubicWeb* version 3 became downloadable for free under the
+terms of the LGPL license. Its community is now steadily growing
+without hampering the fast-paced stream of changes thanks to the time
+and energy originally put in the design of the framework.
.. _Narval: http://www.logilab.org/project/narval
--- a/doc/book/en/intro/index.rst Tue Apr 06 18:51:17 2010 +0200
+++ b/doc/book/en/intro/index.rst Tue Apr 06 19:08:07 2010 +0200
@@ -17,5 +17,5 @@
book-map
history
- concepts/index
+ concepts.rst
tutorial/index
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/features_list.rst Tue Apr 06 19:08:07 2010 +0200
@@ -0,0 +1,224 @@
+=================
+CubicWeb features
+=================
+
+This page tries to resume features found in the bare cubicweb framework,
+how mature and documented they are.
+
+:code maturity (CM):
+
+ - 0: experimental, not ready at all for production, may be killed
+
+ - 1: draft / unsatisfying, api may change in a near future, much probably in long
+ term
+
+ - 2: good enough, api sounds good but will probably evolve a bit with more
+ hindsight
+
+ - 3: mature, backward incompatible changes unexpected (may still evolve though,
+ of course)
+
+
+:documentation level (DL):
+
+ - 0: no documentation
+
+ - 1: poor documentation
+
+ - 2: some valuable documentation but some parts keep uncovered
+
+ - 3: good / complete documentation
+
+
+Instance configuration and maintainance
+=======================================
+
++====================================================================+====+====+
+| FEATURE | CM | DL |
++====================================================================+====+====+
+| setup - installation | 2 | 3 |
+| setup - environment variables | 3 | 2 |
+| setup - running modes | 2 | 2 |
+| setup - administration tasks | 2 | 2 |
+| setup - configuration file | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| configuration - user / groups handling | 3 | 1 |
+| configuration - site configuration | 3 | 1 |
+| configuration - distributed configuration | 2 | 1 |
+| configuration - pyro | 2 | 2 |
++--------------------------------------------------------------------+----+----+
+| multi-sources - capabilities | NA | 0 |
+| multi-sources - configuration | 2 | 0 |
+| multi-sources - ldap integration | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| usage - custom ReST markup | 2 | 0 |
+| usage - personal preferences | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+
+
+Core development
+================
+
++====================================================================+====+====+
+| FEATURE | CM | DL |
++====================================================================+====+====+
+| base - concepts | NA | 3 |
+| base - security model | NA | 2 |
+| base - database initialization | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| rql - base | 2 | 2 |
+| rql - write | 2 | 2 |
+| rql - function | 2 | 0 |
+| rql - outer joins | 2 | 1 |
+| rql - aggregates | 2 | 1 |
+| rql - subqueries | 2 | 0 |
++--------------------------------------------------------------------+----+----+
+| schema - base | 2 | 3 |
+| schema - constraints | 3 | 2 |
+| schema - security | 2 | 2 |
+| schema - inheritance | 1 | 1 |
+| schema - customization | 1 | 1 |
+| schema - introspection | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| vregistry - appobject | 2 | 2 |
+| vregistry - registration | 2 | 2 |
+| vregistry - selection | 3 | 2 |
+| vregistry - core selectors | 3 | 3 |
+| vregistry - custom selectors | 2 | 1 |
+| vregistry - debugging selection | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| entities - interfaces | 2 | ? |
+| entities - customization (dc_,...) | 2 | ? |
+| entities - app logic | 2 | 2 |
+| entities - orm configuration | 2 | 1 |
+| entities - pluggable mixins | 1 | 0 |
+| entities - workflow | 3 | 2 |
++--------------------------------------------------------------------+----+----+
+| dbapi - connection | 3 | 1 |
+| dbapi - data management | 1 | 1 |
+| dbapi - result set | 3 | 1 |
+| dbapi - transaction, undo | 2 | 0 |
++--------------------------------------------------------------------+----+----+
+| cube - layout | 2 | 3 |
+| cube - new cube | 2 | 2 |
++--------------------------------------------------------------------+----+----+
+| migration - context | 2 | 1 |
+| migration - commands | 2 | 2 |
++--------------------------------------------------------------------+----+----+
+| testlib - CubicWebTC | 2 | 1 |
+| testlib - automatic tests | 2 | 2 |
++--------------------------------------------------------------------+----+----+
+| i18n - mark string | 3 | 2 |
+| i18n - customize strings from other cubes / cubicweb | 3 | 1 |
+| i18n - update catalog | 3 | 2 |
++--------------------------------------------------------------------+----+----+
+| more - reloading tips | NA | 0 |
+| more - site_cubicweb | 2 | ? |
+| more - adding options in configuration file | 3 | 0 |
+| more - adding options in site configuration / preferences | 3 | ? |
+| more - optimizing / profiling | 2 | 1 |
+| more - c-c plugins | 3 | 0 |
+| more - crypto services | 0 | 0 |
+| more - massive import | 2 | 0 |
+| more - mime type based conversion | 2 | 0 |
+| more - CWCache | 1 | 0 |
++--------------------------------------------------------------------+----+----+
+
+
+Web UI development
+==================
+
++====================================================================+====+====+
+| FEATURE | CM | DL |
++====================================================================+====+====+
+| base - web request | 2 | 2 |
+| base - exceptions | 2 | 0 |
+| base - session, authentication | 1 | 0 |
+| base - http caching | 2 | 1 |
+| base - external resources | 2 | 2 |
+| base - static files | 2 | ? |
+| base - data sharing | 2 | 2 |
+| base - graphical chart customization | 1 | 1 |
++--------------------------------------------------------------------+----+----+
+| publishing - cycle | 2 | 2 |
+| publishing - error handling | 2 | 1 |
+| publishing - transactions | NA | ? |
++--------------------------------------------------------------------+----+----+
+| controller - base | 2 | 2 |
+| controller - view | 2 | 1 |
+| controller - edit | 2 | 1 |
+| controller - json | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| views - base | 2 | 2 |
+| views - templates | 2 | 2 |
+| views - boxes | 2 | 1 |
+| views - components | 2 | 1 |
+| views - primary | 2 | 1 |
+| views - tabs | 2 | 1 |
+| views - xml | 2 | 0 |
+| views - text | 2 | 1 |
+| views - table | 2 | 1 |
+| views - plot | 2 | 0 |
+| views - navigation | 2 | 0 |
+| views - calendar, timeline | 2 | 0 |
+| views - index | 2 | 2 |
+| views - breadcrumbs | 2 | 1 |
+| views - actions | 2 | 1 |
+| views - debugging | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+| form - base | 2 | 1 |
+| form - fields | 2 | 1 |
+| form - widgets | 2 | 1 |
+| form - captcha | 2 | 0 |
+| form - renderers | 2 | 0 |
+| form - validation error handling | 2 | 0 |
+| form - autoform | 2 | 2 |
+| form - reledit | 2 | 0 |
++--------------------------------------------------------------------+----+----+
+| facets - base | 2 | ? |
+| facets - configuration | 2 | 1 |
+| facets - custom facets | 2 | 0 |
++--------------------------------------------------------------------+----+----+
+| css - base | 1 | 1 |
+| css - customization | 1 | 1 |
++--------------------------------------------------------------------+----+----+
+| js - base | 1 | 1 |
+| js - jquery | 1 | 1 |
+| js - base functions | 1 | 0 |
+| js - ajax | 1 | 0 |
+| js - widgets | 1 | 1 |
++--------------------------------------------------------------------+----+----+
+| other - page template | 0 | 0 |
+| other - inline doc (wdoc) | 2 | 0 |
+| other - magic search | 2 | 0 |
+| other - url mapping | 1 | 1 |
+| other - apache style url rewrite | 1 | 1 |
+| other - sparql | 1 | 0 |
+| other - bookmarks | 2 | 1 |
++--------------------------------------------------------------------+----+----+
+
+
+Repository development
+======================
+
++====================================================================+====+====+
+| FEATURE | CM | DL |
++====================================================================+====+====+
+| base - session | 2 | 2 |
+| base - more security control | 2 | 0 |
+| base - debugging | 2 | 0 |
++--------------------------------------------------------------------+----+----+
+| hooks - development | 2 | 2 |
+| hooks - abstract hooks | 2 | 0 |
+| hooks - core hooks | 2 | 0 |
+| hooks - control | 2 | 0 |
+| hooks - operation | 2 | 2 |
++--------------------------------------------------------------------+----+----+
+| notification - sending email | 2 | ? |
+| notification - base views | 1 | ? |
+| notification - supervisions | 1 | 0 |
++--------------------------------------------------------------------+----+----+
+| source - storages | 2 | 0 |
+| source - authentication plugins | 2 | 0 |
+| source - custom sources | 2 | 0 |
++--------------------------------------------------------------------+----+----+
--- a/entity.py Tue Apr 06 18:51:17 2010 +0200
+++ b/entity.py Tue Apr 06 19:08:07 2010 +0200
@@ -882,7 +882,8 @@
def set_attributes(self, **kwargs):
_check_cw_unsafe(kwargs)
assert kwargs
- assert self._is_saved
+ assert self._is_saved, "should not call set_attributes while entity "\
+ "hasn't been saved yet"
relations = []
for key in kwargs:
relations.append('X %s %%(%s)s' % (key, key))
@@ -900,7 +901,7 @@
"""add relations to the given object. To set a relation where this entity
is the object of the relation, use 'reverse_'<relation> as argument name.
- Values may be an entity, a list of entity, or None (meaning that all
+ Values may be an entity, a list of entities, or None (meaning that all
relations of the given type from or to this object should be deleted).
"""
# XXX update cache
--- a/schema.py Tue Apr 06 18:51:17 2010 +0200
+++ b/schema.py Tue Apr 06 19:08:07 2010 +0200
@@ -400,7 +400,9 @@
__permissions__=RO_ATTR_PERMS)
self.schema.add_relation_def(rdef)
elif not need_has_text and has_has_text:
- self.schema.del_relation_def(self.type, 'has_text', 'String')
+ # use rschema.del_relation_def and not schema.del_relation_def to
+ # avoid deleting the relation type accidentally...
+ self.schema['has_text'].del_relation_def(self, self.schema['String'])
def schema_entity(self):
"""return True if this entity type is used to build the schema"""
--- a/selectors.py Tue Apr 06 18:51:17 2010 +0200
+++ b/selectors.py Tue Apr 06 19:08:07 2010 +0200
@@ -1,42 +1,184 @@
-"""This file contains some basic selectors required by application objects.
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+""".. _Selectors:
+
+Selectors
+---------
+
+Using and combining existant selectors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can combine selectors using the `&`, `|` and `~` operators.
+
+When two selectors are combined using the `&` operator (formerly `chainall`), it
+means that both should return a positive score. On success, the sum of scores is returned.
+
+When two selectors are combined using the `|` operator (former `chainfirst`), it
+means that one of them should return a positive score. On success, the first
+positive score is returned.
+
+You can also "negate" a selector by precedeing it by the `~` unary operator.
+
+Of course you can use parens to balance expressions.
+
+.. Note:
+ When one chains selectors, the final score is the sum of the score of each
+ individual selector (unless one of them returns 0, in which case the object is
+ non selectable)
+
+
+Example
+~~~~~~~
+
+The goal: when on a Blog, one wants the RSS link to refer to blog entries, not to
+the blog entity itself.
+
+To do that, one defines a method on entity classes that returns the RSS stream
+url for a given entity. The default implementation on
+:class:`~cubicweb.entities.AnyEntity` (the generic entity class used as base for
+all others) and a specific implementation on Blog will do what we want.
-A selector is responsible to score how well an object may be used with a
-given context by returning a score.
+But when we have a result set containing several Blog entities (or different
+entities), we don't know on which entity to call the aforementioned method. In
+this case, we keep the generic behaviour.
+
+Hence we have two cases here, one for a single-entity rsets, the other for
+multi-entities rsets.
+
+In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
+
+.. sourcecode:: python
+
+ class RSSIconBox(ExtResourcesBoxTemplate):
+ '''just display the RSS icon on uniform result set'''
+ __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
+
+It takes into account:
+
+* the inherited selection criteria (one has to look them up in the class
+ hierarchy to know the details)
+
+* :class:`~cubicweb.selectors.non_final_entity`, which filters on result sets
+ containing non final entities (a 'final entity' being synonym for entity
+ attributes type, eg `String`, `Int`, etc)
-In CubicWeb Usually the context consists for a request object, a result set
-or None, a specific row/col in the result set, etc...
+This matches our second case. Hence we have to provide a specific component for
+the first case:
+
+.. sourcecode:: python
+
+ class EntityRSSIconBox(RSSIconBox):
+ '''just display the RSS icon on uniform result set for a single entity'''
+ __select__ = RSSIconBox.__select__ & one_line_rset()
+
+Here, one adds the :class:`~cubicweb.selectors.one_line_rset` selector, which
+filters result sets of size 1. Thus, on a result set containing multiple
+entities, :class:`one_line_rset` makes the EntityRSSIconBox class non
+selectable. However for a result set with one entity, the `EntityRSSIconBox`
+class will have a higher score than `RSSIconBox`, which is what we wanted.
+
+Of course, once this is done, you have to:
+
+* fill in the call method of `EntityRSSIconBox`
+
+* provide the default implementation of the method returning the RSS stream url
+ on :class:`~cubicweb.entities.AnyEntity`
+
+* redefine this method on `Blog`.
-If you have trouble with selectors, especially if the objet (typically
-a view or a component) you want to use is not selected and you want to
-know which one(s) of its selectors fail (e.g. returns 0), you can use
-`traced_selection` or even direclty `TRACED_OIDS`.
+When to use selectors?
+~~~~~~~~~~~~~~~~~~~~~~
+
+Selectors are to be used whenever arises the need of dispatching on the shape or
+content of a result set or whatever else context (value in request form params,
+authenticated user groups, etc...). That is, almost all the time.
+
+Here is a quick example:
+
+.. sourcecode:: python
+
+ class UserLink(component.Component):
+ '''if the user is the anonymous user, build a link to login else a link
+ to the connected user object with a loggout link
+ '''
+ __regid__ = 'loggeduserlink'
-`TRACED_OIDS` is a tuple of traced object ids. The special value
-'all' may be used to log selectors for all objects.
+ def call(self):
+ if self._cw.cnx.anonymous_connection:
+ # display login link
+ ...
+ else:
+ # display a link to the connected user object with a loggout link
+ ...
+
+The proper way to implement this with |cubicweb| is two have two different
+classes sharing the same identifier but with different selectors so you'll get
+the correct one according to the context:
+
-For instance, say that the following code yields a `NoSelectableObject`
-exception::
+ class UserLink(component.Component):
+ '''display a link to the connected user object with a loggout link'''
+ __regid__ = 'loggeduserlink'
+ __select__ = component.Component.__select__ & authenticated_user()
- self.view('calendar', myrset)
+ def call(self):
+ # display useractions and siteactions
+ ...
+
+ class AnonUserLink(component.Component):
+ '''build a link to login'''
+ __regid__ = 'loggeduserlink'
+ __select__ = component.Component.__select__ & anonymous_user()
-You can log the selectors involved for *calendar* by replacing the line
-above by::
+ def call(self):
+ # display login link
+ ...
+
+The big advantage, aside readibily once you're familiar with the system, is that
+your cube becomes much more easily customizable by improving componentization.
+
+
+.. _CustomSelectors:
- from cubicweb.selectors import traced_selection
- with traced_selection():
- self.view('calendar', myrset)
+Defining your own selectors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autodocstring:: cubicweb.appobject::objectify_selector
-With python 2.5, think to add:
+In other case, you can take a look at the following abstract base classes:
+
+.. autoclass:: cubicweb.selectors.ExpectedValueSelector
+.. autoclass:: cubicweb.selectors.EClassSelector
+.. autoclass:: cubicweb.selectors.EntitySelector
- from __future__ import with_statement
+Also, think to use the :func:`lltrace` decorator on your selector class' :meth:`__call__` method
+or below the :func:`objectify_selector` decorator of your selector function so it gets
+traceable when :class:`traced_selection` is activated (see :ref:`DebuggingSelectors`).
+
+.. autofunction:: cubicweb.selectors.lltrace
-at the top of your module.
+.. Note::
+ Selectors __call__ should *always* return a positive integer, and shall never
+ return `None`.
+
+
+.. _DebuggingSelectors:
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+Debugging selection
+~~~~~~~~~~~~~~~~~~~
+
+Once in a while, one needs to understand why a view (or any application object)
+is, or is not selected appropriately. Looking at which selectors fired (or did
+not) is the way. The :class:`cubicweb.selectors.traced_selection` context
+manager to help with that, *if you're running your instance in debug mode*.
+
+.. autoclass:: cubicweb.selectors.traced_selection
+
+
+.. |cubicweb| replace:: *CubicWeb*
"""
__docformat__ = "restructuredtext en"
@@ -74,6 +216,9 @@
print '%s -> %s for %s(%s)' % (selname, ret, vobj, vobj.__regid__)
def lltrace(selector):
+ """use this decorator on your selectors so the becomes traceable with
+ :class:`traced_selection`
+ """
# don't wrap selectors if not in development mode
if CubicWebConfiguration.mode == 'system': # XXX config.debug
return selector
@@ -87,19 +232,31 @@
return traced
class traced_selection(object):
- """selector debugging helper.
-
+ """
Typical usage is :
- >>> with traced_selection():
- ... # some code in which you want to debug selectors
- ... # for all objects
+ .. sourcecode:: python
+
+ >>> from cubicweb.selectors import traced_selection
+ >>> with traced_selection():
+ ... # some code in which you want to debug selectors
+ ... # for all objects
+
+ Don't forget the 'from __future__ import with_statement' at the module top-level
+ if you're using python prior to 2.6.
- or
+ This will yield lines like this in the logs::
+
+ selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
- >>> with traced_selection( ('oid1', 'oid2') ):
- ... # some code in which you want to debug selectors
- ... # for objects with id 'oid1' and 'oid2'
+ You can also give to :class:`traced_selection` the identifiers of objects on
+ which you want to debug selection ('oid1' and 'oid2' in the example above).
+
+ .. sourcecode:: python
+
+ >>> with traced_selection( ('oid1', 'oid2') ):
+ ... # some code in which you want to debug selectors
+ ... # for objects with id 'oid1' and 'oid2'
"""
def __init__(self, traced='all'):
@@ -261,12 +418,12 @@
- `accept_none` is False and some cell in the column has a None value
(this may occurs with outer join)
- .. note::
- using EntitySelector or EClassSelector as base selector class impacts
- performance, since when no entity or row is specified the later works on
- every different *entity class* found in the result set, while the former
- works on each *entity* (eg each row of the result set), which may be much
- more costly.
+ .. Note::
+ using :class:`EntitySelector` or :class:`EClassSelector` as base selector
+ class impacts performance, since when no entity or row is specified the
+ later works on every different *entity class* found in the result set,
+ while the former works on each *entity* (eg each row of the result set),
+ which may be much more costly.
"""
@lltrace
@@ -307,8 +464,12 @@
class ExpectedValueSelector(Selector):
- """Take a list of expected values as initializer argument, check
- _get_value method return one of these expected values.
+ """Take a list of expected values as initializer argument and store them
+ into the :attr:`expected` set attribute.
+
+ You should implements the :meth:`_get_value(cls, req, **kwargs)` method
+ which should return the value for the given context. The selector will then
+ return 1 if the value is expected, else 0.
"""
def __init__(self, *expected):
assert expected, self
@@ -346,10 +507,12 @@
class appobject_selectable(Selector):
- """return 1 if another appobject is selectable using the same input context.
+ """Return 1 if another appobject is selectable using the same input context.
Initializer arguments:
+
* `registry`, a registry name
+
* `regid`, an object identifier in this registry
"""
def __init__(self, registry, regid):
--- a/server/migractions.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/migractions.py Tue Apr 06 19:08:07 2010 +0200
@@ -1167,9 +1167,8 @@
return session
return self.cnx.request()
- def cmd_create_entity(self, etype, **kwargs):
+ def cmd_create_entity(self, etype, commit=False, **kwargs):
"""add a new entity of the given type"""
- commit = kwargs.pop('commit', False)
entity = self._cw.create_entity(etype, **kwargs)
if commit:
self.commit()
--- a/server/session.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/session.py Tue Apr 06 19:08:07 2010 +0200
@@ -896,7 +896,8 @@
# deprecated ###############################################################
@deprecated("[3.7] execute is now unsafe by default in hooks/operation. You"
- " can also control security with session.[read|write]_security")
+ " can also control security with the security_enabled context "
+ "manager")
def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
propagate=False):
"""like .execute but with security checking disabled (this method is
--- a/server/sources/__init__.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/sources/__init__.py Tue Apr 06 19:08:07 2010 +0200
@@ -100,7 +100,7 @@
"""method called by the repository once ready to handle request"""
pass
- def backup(self, backupfile):
+ def backup(self, backupfile, confirm):
"""method called to create a backup of source's data"""
pass
--- a/server/sources/ldapuser.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/sources/ldapuser.py Tue Apr 06 19:08:07 2010 +0200
@@ -64,7 +64,7 @@
{'type' : 'choice',
'default': 'ldap',
'choices': ('ldap', 'ldaps', 'ldapi'),
- 'help': 'ldap protocol',
+ 'help': 'ldap protocol (allowed values: ldap, ldaps, ldapi)',
'group': 'ldap-source', 'inputlevel': 1,
}),
@@ -200,6 +200,7 @@
except KeyError:
return # no email in ldap, we're done
session = self.repo.internal_session()
+ execute = session.execute
try:
cursor = session.system_sql("SELECT eid, extid FROM entities WHERE "
"source='%s'" % self.uri)
@@ -210,20 +211,29 @@
if res:
ldapemailaddr = res[0].get(ldap_emailattr)
if ldapemailaddr:
- rset = session.execute('EmailAddress A WHERE '
- 'U use_email X, U eid %(u)s',
- {'u': eid})
+ rset = execute('Any X,A WHERE '
+ 'X address A, U use_email X, U eid %(u)s',
+ {'u': eid})
ldapemailaddr = unicode(ldapemailaddr)
- for emailaddr, in rset:
+ for emaileid, emailaddr, in rset:
if emailaddr == ldapemailaddr:
break
else:
self.info('updating email address of user %s to %s',
extid, ldapemailaddr)
- if rset:
- session.execute('SET X address %(addr)s WHERE '
- 'U primary_email X, U eid %(u)s',
- {'addr': ldapemailaddr, 'u': eid})
+ emailrset = execute('EmailAddress A WHERE A address %(addr)s',
+ {'addr': ldapemailaddr})
+ if emailrset:
+ execute('SET U use_email X WHERE '
+ 'X eid %(x)s, U eid %(u)s',
+ {'x': emailrset[0][0], 'u': eid})
+ elif rset:
+ if not execute('SET X address %(addr)s WHERE '
+ 'U primary_email X, U eid %(u)s',
+ {'addr': ldapemailaddr, 'u': eid}, 'u'):
+ execute('SET X address %(addr)s WHERE '
+ 'X eid %(x)s',
+ {'addr': ldapemailaddr, 'x': rset[0][0]}, 'x')
else:
# no email found, create it
_insert_email(session, ldapemailaddr, eid)
--- a/server/sources/native.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/sources/native.py Tue Apr 06 19:08:07 2010 +0200
@@ -471,21 +471,23 @@
# on the filesystem. To make the entity.data usage absolutely
# transparent, we'll have to reset entity.data to its binary
# value once the SQL query will be executed
- orig_values = {}
+ restore_values = {}
etype = entity.__regid__
for attr, storage in self._storages.get(etype, {}).items():
try:
if attr in entity.edited_attributes:
- orig_values[attr] = entity[attr]
handler = getattr(storage, 'entity_%s' % event)
- handler(entity, attr)
+ real_value = handler(entity, attr)
+ restore_values[attr] = real_value
except AttributeError:
assert event == 'deleted'
getattr(storage, 'entity_deleted')(entity, attr)
- yield # 2/ execute the source's instructions
- # 3/ restore original values
- for attr, value in orig_values.items():
- entity[attr] = value
+ try:
+ yield # 2/ execute the source's instructions
+ finally:
+ # 3/ restore original values
+ for attr, value in restore_values.items():
+ entity[attr] = value
def add_entity(self, session, entity):
"""add a new entity to the source"""
--- a/server/sources/storages.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/sources/storages.py Tue Apr 06 19:08:07 2010 +0200
@@ -72,28 +72,23 @@
def entity_added(self, entity, attr):
"""an entity using this storage for attr has been added"""
- if not entity._cw.transaction_data.get('fs_importing'):
- try:
- value = entity.pop(attr)
- except KeyError:
- pass
- else:
- fpath = self.new_fs_path(entity, attr)
- # bytes storage used to store file's path
- entity[attr] = Binary(fpath)
- file(fpath, 'w').write(value.getvalue())
- AddFileOp(entity._cw, filepath=fpath)
- # else entity[attr] is expected to be an already existant file path
+ if entity._cw.transaction_data.get('fs_importing'):
+ binary = Binary(file(entity[attr].getvalue()).read())
+ else:
+ binary = entity.pop(attr)
+ fpath = self.new_fs_path(entity, attr)
+ # bytes storage used to store file's path
+ entity[attr] = Binary(fpath)
+ file(fpath, 'w').write(binary.getvalue())
+ AddFileOp(entity._cw, filepath=fpath)
+ return binary
def entity_updated(self, entity, attr):
"""an entity using this storage for attr has been updatded"""
- try:
- value = entity.pop(attr)
- except KeyError:
- pass
- else:
- fpath = self.current_fs_path(entity, attr)
- UpdateFileOp(entity._cw, filepath=fpath, filedata=value.getvalue())
+ binary = entity.pop(attr)
+ fpath = self.current_fs_path(entity, attr)
+ UpdateFileOp(entity._cw, filepath=fpath, filedata=binary.getvalue())
+ return binary
def entity_deleted(self, entity, attr):
"""an entity using this storage for attr has been deleted"""
@@ -110,8 +105,10 @@
cu = sysource.doexec(entity._cw,
'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % (
attr, entity.__regid__, entity.eid))
- BINARY = sysource.dbhelper.dbapi_module.BINARY
- return sysource._process_value(cu.fetchone()[0], [None, BINARY],
+ rawvalue = cu.fetchone()[0]
+ if rawvalue is None: # no previous value
+ return self.new_fs_path(entity, attr)
+ return sysource._process_value(rawvalue, cu.description[0],
binarywrap=str)
--- a/server/test/unittest_repository.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/test/unittest_repository.py Tue Apr 06 19:08:07 2010 +0200
@@ -396,7 +396,8 @@
self.entity.set_attributes(alias=u'foo')
with self.temporary_appobjects(DummyBeforeHook):
req = self.request()
- self.assertRaises(RepositoryError, req.create_entity,
+ # XXX will fail with python -O
+ self.assertRaises(AssertionError, req.create_entity,
'EmailAddress', address=u'a@b.fr')
--- a/server/test/unittest_storage.py Tue Apr 06 18:51:17 2010 +0200
+++ b/server/test/unittest_storage.py Tue Apr 06 19:08:07 2010 +0200
@@ -39,7 +39,6 @@
oldvalue = self._cw.transaction_data['orig_file_value']
assert oldvalue == self.entity.data.getvalue()
-
class StorageTC(CubicWebTC):
def setup_database(self):
@@ -88,11 +87,12 @@
def test_bfss_fs_importing_doesnt_touch_path(self):
self.session.transaction_data['fs_importing'] = True
- f1 = self.session.create_entity('File', data=Binary('/the/path'),
+ filepath = osp.abspath(__file__)
+ f1 = self.session.create_entity('File', data=Binary(filepath),
data_format=u'text/plain', data_name=u'foo')
fspath = self.execute('Any fspath(D) WHERE F eid %(f)s, F data D',
{'f': f1.eid})[0][0]
- self.assertEquals(fspath.getvalue(), '/the/path')
+ self.assertEquals(fspath.getvalue(), filepath)
def test_source_storage_transparency(self):
with self.temporary_appobjects(DummyBeforeHook, DummyAfterHook):
@@ -156,5 +156,29 @@
{'x': f1.eid}, 'x')
self.assertEquals(str(ex), 'UPPER can not be called on mapped attribute')
+
+ def test_bfss_fs_importing_transparency(self):
+ self.session.transaction_data['fs_importing'] = True
+ filepath = osp.abspath(__file__)
+ f1 = self.session.create_entity('File', data=Binary(filepath),
+ data_format=u'text/plain', data_name=u'foo')
+ self.assertEquals(f1.data.getvalue(), file(filepath).read(),
+ 'files content differ')
+
+
+ def test_bfss_update_with_existing_data(self):
+ # use self.session to use server-side cache
+ f1 = self.session.create_entity('File', data=Binary('some data'),
+ data_format=u'text/plain', data_name=u'foo')
+ # NOTE: do not use set_attributes() which would automatically
+ # update f1's local dict. We want the pure rql version to work
+ self.execute('SET F data %(d)s WHERE F eid %(f)s',
+ {'d': Binary('some other data'), 'f': f1.eid})
+ self.assertEquals(f1.data.getvalue(), 'some other data')
+ self.commit()
+ f2 = self.entity('Any F WHERE F eid %(f)s, F is File', {'f': f1.eid})
+ self.assertEquals(f2.data.getvalue(), 'some other data')
+
+
if __name__ == '__main__':
unittest_main()
--- a/vregistry.py Tue Apr 06 18:51:17 2010 +0200
+++ b/vregistry.py Tue Apr 06 19:08:07 2010 +0200
@@ -161,11 +161,12 @@
# dynamic selection methods ################################################
def object_by_id(self, oid, *args, **kwargs):
- """return object with the given oid. Only one object is expected to be
- found.
+ """return object with the `oid` identifier. Only one object is expected
+ to be found.
- raise `ObjectNotFound` if not object with id <oid> in <registry>
- raise `AssertionError` if there is more than one object there
+ raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
+
+ raise :exc:`AssertionError` if there is more than one object there
"""
objects = self[oid]
assert len(objects) == 1, objects
@@ -175,8 +176,9 @@
"""return the most specific object among those with the given oid
according to the given context.
- raise `ObjectNotFound` if not object with id <oid> in <registry>
- raise `NoSelectableObject` if not object apply
+ raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
+
+ raise :exc:`NoSelectableObject` if not object apply
"""
return self._select_best(self[oid], *args, **kwargs)
@@ -318,6 +320,20 @@
# self[regname].pop(oid, None)
def register_all(self, objects, modname, butclasses=()):
+ """register all `objects` given. Objects which are not from the module
+ `modname` or which are in `butclasses` won't be registered.
+
+ Typical usage is:
+
+ .. sourcecode:: python
+
+ vreg.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))
+
+ So you get partially automatic registration, keeping manual registration
+ for some object (to use
+ :meth:`~cubicweb.cwvreg.CubicWebRegistry.register_and_replace` for
+ instance)
+ """
for obj in objects:
try:
if obj.__module__ != modname or obj in butclasses:
@@ -329,7 +345,13 @@
self.register(obj, oid=oid)
def register(self, obj, registryname=None, oid=None, clear=False):
- """base method to add an object in the registry"""
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified, with identifier `oid` or
+ `obj.__regid__` if not specified.
+
+ If `clear` is true, all objects with the same identifier will be
+ previously unregistered.
+ """
assert not '__abstract__' in obj.__dict__
try:
vname = obj.__name__
@@ -344,10 +366,18 @@
self._loadedmods[obj.__module__][classid(obj)] = obj
def unregister(self, obj, registryname=None):
+ """unregister `obj` application object from the registry `registryname` or
+ `obj.__registry__` if not specified.
+ """
for registryname in class_registries(obj, registryname):
self[registryname].unregister(obj)
def register_and_replace(self, obj, replaced, registryname=None):
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified. If found, the `replaced` object
+ will be unregistered first (else a warning will be issued as it's
+ generally unexpected).
+ """
for registryname in class_registries(obj, registryname):
self[registryname].register_and_replace(obj, replaced)
--- a/web/uicfg.py Tue Apr 06 18:51:17 2010 +0200
+++ b/web/uicfg.py Tue Apr 06 19:08:07 2010 +0200
@@ -11,24 +11,25 @@
Primary view configuration
``````````````````````````
-If you want to customize the primary view of an entity, overriding the
-primary view class may not be necessary. For simple adjustments
-(attributes or relations display locations and styles), a much simpler
-way is to use uicfg.
+If you want to customize the primary view of an entity, overriding the primary
+view class may not be necessary. For simple adjustments (attributes or relations
+display locations and styles), a much simpler way is to use uicfg.
Attributes/relations display location
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-In the primary view, there are 3 sections where attributes and
-relations can be displayed (represented in pink in the image below):
- * attributes
- * relations
- * sideboxes
+In the primary view, there are 3 sections where attributes and relations can be
+displayed (represented in pink in the image below):
+
+* attributes
+* relations
+* sideboxes
.. image:: ../../images/primaryview_template.png
-**Attributes** can only be displayed in the attributes section (default behavior). They can also be hidden.
+**Attributes** can only be displayed in the attributes section (default
+ behavior). They can also be hidden.
For instance, to hide the ``title`` attribute of the ``Blog`` entity:
@@ -41,12 +42,14 @@
**Relations** can be either displayed in one of the three sections or hidden.
For relations, there are two methods:
- * ``tag_object_of`` for modifying the primary view of the object
- * ``tag_subject_of`` for modifying the primary view of the subject
+
+* ``tag_object_of`` for modifying the primary view of the object
+* ``tag_subject_of`` for modifying the primary view of the subject
These two methods take two arguments:
- * a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'``
- * the section name or ``hidden``
+
+* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'``
+* the section name or ``hidden``
.. sourcecode:: python
@@ -61,29 +64,31 @@
Display content
^^^^^^^^^^^^^^^
-You can use ``primaryview_display_ctrl`` to customize the display of attributes or relations. Values of ``primaryview_display_ctrl`` are dictionaries.
+You can use ``primaryview_display_ctrl`` to customize the display of attributes
+or relations. Values of ``primaryview_display_ctrl`` are dictionaries.
Common keys for attributes and relations are:
- * ``vid``: specifies the regid of the view for displaying the attribute or the relation.
+* ``vid``: specifies the regid of the view for displaying the attribute or the relation.
- If ``vid`` is not specified, the default value depends on the section:
- * ``attributes`` section: 'reledit' view
- * ``relations`` section: 'autolimited' view
- * ``sideboxes`` section: 'sidebox' view
+ If ``vid`` is not specified, the default value depends on the section:
+ * ``attributes`` section: 'reledit' view
+ * ``relations`` section: 'autolimited' view
+ * ``sideboxes`` section: 'sidebox' view
- * ``order``: int used to control order within a section. When not specified, automatically set according to order in which tags are added.
+* ``order``: int used to control order within a section. When not specified,
+ automatically set according to order in which tags are added.
Keys for relations only:
- * ``label``: label for the relations section or side box
+* ``label``: label for the relations section or side box
- * ``showlabel``: boolean telling whether the label is displayed
+* ``showlabel``: boolean telling whether the label is displayed
- * ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed
+* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed
- * ``filter``: callback taking the related result set as argument and returning it filtered
+* ``filter``: callback taking the related result set as argument and returning it filtered
.. sourcecode:: python
@@ -96,13 +101,17 @@
{'vid': 'list', 'label': _('latest comment(s):'), 'limit': True,
'filter': lambda rset: rset.filtered_rset(lambda x: x.e_schema == 'Comment')})
-.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the object of the relation is ignored for respectively ``tag_object_of`` or ``tag_subject_of``. To avoid warnings during execution, they should be set to ``'*'``.
+.. Warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the
+ object of the relation is ignored for respectively ``tag_object_of`` or
+ ``tag_subject_of``. To avoid warnings during execution, they should be set to
+ ``'*'``.
Index view configuration
````````````````````````
:indexview_etype_section:
entity type category in the index/manage page. May be one of:
+
* ``application``
* ``system``
* ``schema``
@@ -128,46 +137,59 @@
Attributes/relations display location
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-``uicfg.autoform_section`` specifies where to display a relation in creation/edition entity form for a given form type.
-``tag_attribute``, ``tag_subject_of`` and ``tag_object_of`` methods for this
-relation tag expect two arguments additionally to the relation key: a
-``formtype`` and a ``section``.
+``uicfg.autoform_section`` specifies where to display a relation in
+creation/edition entity form for a given form type. ``tag_attribute``,
+``tag_subject_of`` and ``tag_object_of`` methods for this relation tag expect
+two arguments additionally to the relation key: a ``formtype`` and a
+``section``.
formtype may be one of:
- * ``main``, the main entity form (via the modify action)
- * ``inlined``, the form for an entity inlined into another form
- * ``muledit``, the table form to edit multiple entities
+
+* ``main``, the main entity form (via the modify action)
+* ``inlined``, the form for an entity inlined into another form
+* ``muledit``, the table form to edit multiple entities
section may be one of:
- * ``hidden``, don't display
- * ``attributes``, display in the attributes section
- * ``relations``, display in the relations section, using the generic relation
- selector combobox (available in main form only, and not for attribute
- relation)
- * ``inlined``, display target entity of the relation in an inlined form
- (available in main form only, and not for attribute relation)
- * ``metadata``, display in a special metadata form (NOT YET IMPLEMENTED,
- subject to changes)
+
+* ``hidden``, don't display
+
+* ``attributes``, display in the attributes section
+
+* ``relations``, display in the relations section, using the generic relation
+ selector combobox (available in main form only, and not for attribute
+ relation)
-By default, mandatory relations are displayed in the ``attributes`` section, others in ``relations`` section.
+* ``inlined``, display target entity of the relation in an inlined form
+ (available in main form only, and not for attribute relation)
+
+* ``metadata``, display in a special metadata form (NOT YET IMPLEMENTED, subject
+ to changes)
+
+By default, mandatory relations are displayed in the ``attributes`` section,
+others in ``relations`` section.
Change default fields
^^^^^^^^^^^^^^^^^^^^^
Use ``autoform_field`` to replace the default field type of an attribute.
-.. warning: ``autoform_field_kwargs`` should usually be used instead of ``autoform_field``. Do not use both methods for the same relation!
+.. Warning:
+ ``autoform_field_kwargs`` should usually be used instead of
+ ``autoform_field``. Do not use both methods for the same relation!
Customize field options
^^^^^^^^^^^^^^^^^^^^^^^
-In order to customize field options (see :class:`cubicweb.web.formfields.Field` for a detailed list of options), use ``autoform_field_kwargs``. This rtag takes a relation triplet and a dictionary as arguments.
+In order to customize field options (see :class:`cubicweb.web.formfields.Field`
+for a detailed list of options), use ``autoform_field_kwargs``. This rtag takes
+a relation triplet and a dictionary as arguments.
.. sourcecode:: python
# Change the content of the combobox
- # here ``ticket_done_in_choices`` is a function which returns a list of elements to populate the combobox
+ # here ``ticket_done_in_choices`` is a function which returns a list of
+ # elements to populate the combobox
uicfg.autoform_field_kwargs.tag_subject_of(('Ticket', 'done_in', '*'), {'sort': False,
'choices': ticket_done_in_choices})
@@ -176,8 +198,8 @@
Overriding permissions
^^^^^^^^^^^^^^^^^^^^^^
-``autoform_permissions_overrides`` provides a way to by-pass security checking for dark-corner case where it can't
-be verified properly. XXX documents.
+``autoform_permissions_overrides`` provides a way to by-pass security checking
+for dark-corner case where it can't be verified properly. XXX documents.
"""
__docformat__ = "restructuredtext en"
--- a/web/views/basecomponents.py Tue Apr 06 18:51:17 2010 +0200
+++ b/web/views/basecomponents.py Tue Apr 06 19:08:07 2010 +0200
@@ -15,7 +15,8 @@
from logilab.mtconverter import xml_escape
from rql import parse
-from cubicweb.selectors import yes, multi_etypes_rset, match_form_params
+from cubicweb.selectors import (yes, multi_etypes_rset, match_form_params,
+ anonymous_user, authenticated_user)
from cubicweb.schema import display_name
from cubicweb.uilib import toggle_action
from cubicweb.web import component
@@ -79,35 +80,19 @@
self._cw._(u'help'),))
-class UserLink(component.Component):
- """if the user is the anonymous user, build a link to login
- else a link to the connected user object with a loggout link
+class _UserLink(component.Component):
+ """if the user is the anonymous user, build a link to login else display a menu
+ with user'action (preference, logout, etc...)
"""
cw_property_defs = VISIBLE_PROP_DEF
# don't want user to hide this component using an cwproperty
site_wide = True
__regid__ = 'loggeduserlink'
+
+class AnonUserLink(_UserLink):
+ __select__ = _UserLink.__select__ & anonymous_user()
def call(self):
- if not self._cw.cnx.anonymous_connection:
- # display useractions and siteactions
- actions = self._cw.vreg['actions'].possible_actions(self._cw, rset=self.cw_rset)
- box = MenuWidget('', 'userActionsBox', _class='', islist=False)
- menu = PopupBoxMenu(self._cw.user.login, isitem=False)
- box.append(menu)
- for action in actions.get('useractions', ()):
- menu.append(BoxLink(action.url(), self._cw._(action.title),
- action.html_class()))
- if actions.get('useractions') and actions.get('siteactions'):
- menu.append(BoxSeparator())
- for action in actions.get('siteactions', ()):
- menu.append(BoxLink(action.url(), self._cw._(action.title),
- action.html_class()))
- box.render(w=self.w)
- else:
- self.anon_user_link()
-
- def anon_user_link(self):
if self._cw.vreg.config['auth-mode'] == 'cookie':
self.w(self._cw._('anonymous'))
self.w(u''' [<a class="logout" href="javascript: popupLoginBox();">%s</a>]'''
@@ -118,6 +103,26 @@
% (self._cw.build_url('login'), self._cw._('login')))
+class UserLink(_UserLink):
+ __select__ = _UserLink.__select__ & authenticated_user()
+
+ def call(self):
+ # display useractions and siteactions
+ actions = self._cw.vreg['actions'].possible_actions(self._cw, rset=self.cw_rset)
+ box = MenuWidget('', 'userActionsBox', _class='', islist=False)
+ menu = PopupBoxMenu(self._cw.user.login, isitem=False)
+ box.append(menu)
+ for action in actions.get('useractions', ()):
+ menu.append(BoxLink(action.url(), self._cw._(action.title),
+ action.html_class()))
+ if actions.get('useractions') and actions.get('siteactions'):
+ menu.append(BoxSeparator())
+ for action in actions.get('siteactions', ()):
+ menu.append(BoxLink(action.url(), self._cw._(action.title),
+ action.html_class()))
+ box.render(w=self.w)
+
+
class ApplicationMessage(component.Component):
"""display messages given using the __message parameter into a special div
section
--- a/web/views/basecontrollers.py Tue Apr 06 18:51:17 2010 +0200
+++ b/web/views/basecontrollers.py Tue Apr 06 19:08:07 2010 +0200
@@ -277,7 +277,11 @@
args = self._cw.form.get('arg', ())
if not isinstance(args, (list, tuple)):
args = (args,)
- args = [simplejson.loads(arg) for arg in args]
+ try:
+ args = [simplejson.loads(arg) for arg in args]
+ except ValueError, exc:
+ self.exception('error while decoding json arguments for js_%s: %s', fname, args, exc)
+ raise RemoteCallFailed(repr(exc))
try:
result = func(*args)
except (RemoteCallFailed, DirectResponse):
@@ -442,6 +446,7 @@
view = req.vreg['views'].select('doreledit', req, rset=rset, rtype=args['rtype'])
stream = view.set_stream()
view.render(**args)
+ # XXX why not _call_view ?
extresources = req.html_headers.getvalue(skiphead=True)
if extresources:
stream.write(u'<div class="ajaxHtmlHead">\n')
--- a/web/views/tabs.py Tue Apr 06 18:51:17 2010 +0200
+++ b/web/views/tabs.py Tue Apr 06 19:08:07 2010 +0200
@@ -32,9 +32,7 @@
def lazyview(self, vid, rql=None, eid=None, rset=None, tabid=None,
reloadable=False, show_spinbox=True, w=None):
- """a lazy version of wview
- first version only support lazy viewing for an entity at a time
- """
+ """ a lazy version of wview """
w = w or self.w
self._cw.add_js('cubicweb.lazy.js')
urlparams = {'vid' : vid, 'fname' : 'view'}