backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 15 Apr 2010 19:39:55 +0200
changeset 5302 dfd147de06b2
parent 5298 66b0987716f9 (current diff)
parent 5301 f4219a6e62e3 (diff)
child 5321 6bdca255985a
backport stable
__pkginfo__.py
cwctl.py
cwvreg.py
debian/control
etwist/server.py
i18n/en.po
i18n/es.po
i18n/fr.po
req.py
selectors.py
server/hook.py
server/migractions.py
server/repository.py
server/sources/rql2sql.py
web/application.py
web/request.py
web/views/urlpublishing.py
--- a/__pkginfo__.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/__pkginfo__.py	Thu Apr 15 19:39:55 2010 +0200
@@ -7,7 +7,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 7, 3)
+numversion = (3, 7, 4)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/cwvreg.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/cwvreg.py	Thu Apr 15 19:39:55 2010 +0200
@@ -476,7 +476,7 @@
         """set instance'schema and load application objects"""
         self._set_schema(schema)
         # now we can load application's web objects
-        self.reload(self.config.vregistry_path())
+        self.reload(self.config.vregistry_path(), force_reload=False)
         # map lowered entity type names to their actual name
         self.case_insensitive_etypes = {}
         for eschema in self.schema.entities():
@@ -490,21 +490,22 @@
         if self.is_reload_needed(path):
             self.reload(path)
 
-    def reload(self, path):
+    def reload(self, path, force_reload=True):
         """modification detected, reset and reload the vreg"""
         CW_EVENT_MANAGER.emit('before-registry-reload')
-        cleanup_sys_modules(path)
-        cubes = self.config.cubes()
-        # if the fs code use some cubes not yet registered into the instance we
-        # should cleanup sys.modules for those as well to avoid potential bad
-        # class reference pb after reloading
-        cfg = self.config
-        for cube in cfg.expand_cubes(cubes, with_recommends=True):
-            if not cube in cubes:
-                cpath = cfg.build_vregistry_cube_path([cfg.cube_dir(cube)])
-                cleanup_sys_modules(cpath)
+        if force_reload:
+            cleanup_sys_modules(path)
+            cubes = self.config.cubes()
+            # if the fs code use some cubes not yet registered into the instance
+            # we should cleanup sys.modules for those as well to avoid potential
+            # bad class reference pb after reloading
+            cfg = self.config
+            for cube in cfg.expand_cubes(cubes, with_recommends=True):
+                if not cube in cubes:
+                    cpath = cfg.build_vregistry_cube_path([cfg.cube_dir(cube)])
+                    cleanup_sys_modules(cpath)
         self.reset()
-        self.register_objects(path, True)
+        self.register_objects(path, force_reload)
         CW_EVENT_MANAGER.emit('after-registry-reload')
 
     def _set_schema(self, schema):
--- a/debian/changelog	Thu Apr 15 18:54:13 2010 +0200
+++ b/debian/changelog	Thu Apr 15 19:39:55 2010 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.7.4-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 15 Apr 2010 18:20:39 +0200
+
 cubicweb (3.7.3-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Thu Apr 15 18:54:13 2010 +0200
+++ b/debian/control	Thu Apr 15 19:39:55 2010 +0200
@@ -33,7 +33,7 @@
 Conflicts: cubicweb-multisources
 Replaces: cubicweb-multisources
 Provides: cubicweb-multisources
-Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (=> 1.0.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2
+Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.0.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2
 Recommends: pyro, cubicweb-documentation (= ${source:Version})
 Description: server part of the CubicWeb framework
  CubicWeb is a semantic web application framework.
--- a/devtools/htmlparser.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/devtools/htmlparser.py	Thu Apr 15 19:39:55 2010 +0200
@@ -74,6 +74,21 @@
         Validator.__init__(self)
         self.parser = etree.XMLParser()
 
+class XMLDemotingValidator(SaxOnlyValidator):
+    """ some views produce html instead of xhtml, using demote_to_html
+
+    this is typically related to the use of external dependencies
+    which do not produce valid xhtml (google maps, ...)
+    """
+
+    def preprocess_data(self, data):
+        if data.startswith('<?xml'):
+            self.parser = etree.XMLParser()
+        else:
+            self.parser = etree.HTMLParser()
+        return data
+
+
 class HTMLValidator(Validator):
 
     def __init__(self):
--- a/doc/book/en/development/devweb/facets.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/facets.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,12 +1,38 @@
 The facets system
 -----------------
-XXX feed me more (below is the extracted of adim blog)
+
+Facets allow to restrict searches according to some criteria. CubicWeb has a builtin `facet`_ system to define restrictions
+`filters`_ really as easily as possible. A few base classes for facets
+are provided in ``cubicweb.web.facet.py``. All classes inherits from
+the base class ``AbstractFacet``. 
+
+Here is an overview of the facets rendering pick from the `tracker` cube:
+
+.. image:: ../../images/facet_overview.png
+
+Facets will appear on each page presenting more than one entity.
+
 
 
-Recently, for internal purposes, we've made a little cubicweb application to
-help us
-organizing visits to find new office locations. Here's an *excerpt* of the
-schema:
+VocabularyFacet
+~~~~~~~~~~~~~~~~
+The ``VocabularyFacet`` inherits from the ``AbstractFacet``.
+A class which inherits from VocabularyFacets must redefine these methods:
+
+.. automethod:: cubicweb.web.facet.VocabularyFacet.vocabulary
+.. automethod:: cubicweb.web.facet.VocabularyFacet.possible_values
+
+RelationFacet
+~~~~~~~~~~~~~~
+
+The ``RelationFacet`` inherits from the ``VocabularyFacet``. It allows to filter entities according to certain relation's values. Generally, you just have to define some class attributes like:
+
+- rtype: the name of the relation
+- role: the default value is set to `subject`
+- target_attr: needed if it is not the default attribute of the entity
+
+
+To illustrate this facet, let's take for example an *excerpt* of the schema of an office location search application:
 
 .. sourcecode:: python
 
@@ -14,30 +40,25 @@
       price = Int(description='euros / m2 / HC / HT')
       surface = Int(description='m2')
       description = RichString(fulltextindexed=True)
-      has_address = SubjectRelation('PostalAddress', cardinality='1?', composite='subject')
+      has_address = SubjectRelation('PostalAddress',
+                                    cardinality='1?',
+                                    composite='subject')
       proposed_by = SubjectRelation('Agency')
-      comments = ObjectRelation('Comment', cardinality='1*', composite='object')
-      screenshots = SubjectRelation(('File', 'Image'), cardinality='*1',
+      comments = ObjectRelation('Comment',
+                                cardinality='1*',
+                                composite='object')
+      screenshots = SubjectRelation(('File', 'Image'),
+                                    cardinality='*1',
                                     composite='subject')
 
-The two other entity types defined in the schema are `Visit` and `Agency` but we
-can also guess from the above that this application uses the two cubes
-`comment`_ and
-`addressbook`_ (remember, cubicweb is only a game where you assemble cubes !).
 
-While we know that just defining the schema in enough to have a full, usable,
-(testable !) application, we also know that every application needs to be
-customized to fulfill the needs it was built for. So in this case, what we
-needed most was some custom filters that would let us restrict searches
-according
-to surfaces, prices or zipcodes. Fortunately for us, Cubicweb provides the
-**facets** (image_) mechanism and a few base classes that make the task quite
-easy:
+We define a facet to filter offices according to the attribute
+`postalcode` of their associated `PostalAdress`.
 
 .. sourcecode:: python
 
   class PostalCodeFacet(RelationFacet):
-      __regid__ = 'postalcode-facet'             # every registered class must have an id
+      __regid__ = 'postalcode-facet'      # every registered class must have an id
       __select__ = implements('Office')   # this facet should only be selected when
                                           # visualizing offices
       rtype = 'has_address'               # this facet is a filter on the entity linked to
@@ -46,18 +67,19 @@
       target_attr = 'postalcode'          # the filter's key is the attribute "postal_code"
                                           # of the target PostalAddress entity
 
-This is a typical `RelationFacet`: we want to be able to filter offices
-according
-to the attribute `postalcode` of their associated `PostalAdress`. Each line in
-the class is explained by the comment on its right.
+
+AttributeFacet
+~~~~~~~~~~~~~~
 
-Now, here is the code to define a filter based on the `surface` attribute of the
-`Office`:
+The ``AttributeFacet`` inherits from the ``RelationFacet``. It allows to filter entities according to certain attribute's values.
+
+The example below resumes the former schema. We define now a filter based on the `surface` attribute of the
+`Office`.
 
 .. sourcecode:: python
 
   class SurfaceFacet(AttributeFacet):
-      __regid__ = 'surface-facet'              # every registered class must have an id
+      __regid__ = 'surface-facet'       # every registered class must have an id
       __select__ = implements('Office') # this facet should only be selected when
                                         # visualizing offices
       rtype = 'surface'                 # the filter's key is the attribute "surface"
@@ -75,35 +97,30 @@
           return [('> 200', '200'), ('> 250', '250'),
                   ('> 275', '275'), ('> 300', '300')]
 
-
-And that's it: we have two filter boxes automatically displayed on each page
-presenting more than one office. The `price` facet is basically the same as the
-`surface` one but with a different vocabulary and with ``rtype = 'price'``.
+RangeFacet
+~~~~~~~~~~
+The ``RangeFacet`` inherits from the ``AttributeFacet``. It allows to filter entities according to certain attributes of numerical type.
 
-(The cube also benefits from the builtin google map views defined by
-cubicweb but that's for another blog).
+The ``RangeFacet`` displays a slider using `jquery`_ to choose a lower bound and an upper bound.
 
-.. _image: http://www.cubicweb.org/image/197646?vid=download
-.. _comment: http://www.cubicweb.org/project/cubicweb-comment
-.. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook
+The example below defines a facet to filter a selection of books according to their number of pages.
 
-CubicWeb has this really nice builtin `facet`_ system to
-define restrictions `filters`_ really as easily as possible.
+.. sourcecode:: python
 
-We've just added two new kind of facets in CubicWeb :
+   class BookPagesFacet(RangeFacet):
+       __regid__ = 'priority-facet'
+       __select__ = RangeFacet.__select__ & implements('Book')
+       rtype = 'pages'
 
-- The **RangeFacet** which displays a slider using `jquery`_
-  to choose a lower bound and an upper bound. The **RangeWidget**
-  works with either numerical values or date values
+The image below display the rendering of the ``RangeFacet``:
 
-- The **HasRelationFacet** which displays a simple checkbox and
-  lets you refine your selection in order to get only entities
-  that actually use this relation.
+.. image:: ../../images/facet_range.png
 
-.. image :: http://www.cubicweb.org/Image/343498?vid=download
+DateRangeFacet
+~~~~~~~~~~~~~~
+The ``DateRangeFacet`` inherits from the ``RangeFacet``. It allows to filter entities according to certain attributes of date type.
 
-
-Here's an example of code that defines a facet to filter
+Here is an example of code that defines a facet to filter
 musical works according to their composition date:
 
 .. sourcecode:: python
@@ -116,14 +133,39 @@
         # 3. specify the attribute name that actually stores the date in the DB
         rtype = 'composition_date'
 
-And that's it, on each page displaying tracks, you'll be able to filter them
+With this facet, on each page displaying tracks, you'll be able to filter them
 according to their composition date with a jquery slider.
 
-All this, brought by CubicWeb (in the next 3.3 version)
+The image below display the rendering of the ``DateRangeFacet``:
+
+.. image:: ../../images/facet_date_range.png
+
+
+HasRelationFacet
+~~~~~~~~~~~~~~~~
+
+The ``DateRangeFacet`` inherits from the ``AbstractFacet``. It will
+display a simple checkbox and lets you refine your selection in order
+to get only entities that actually use this relation.
+
+Here is an example of the rendering of the ``HasRelationFacet`` to
+filter entities with image and the corresponding code:
+
+.. image:: ../../images/facet_has_image.png
+
+.. sourcecode:: python
+
+  class HasImageFacet(HasRelationFacet):
+      __regid__ = 'hasimage-facet'
+      __select__ = HasRelationFacet.__select__ & implements('Book')
+      rtype = 'has_image'
+
+
+
+To use ``HasRelationFacet`` on a reverse relation add ``role = 'object'`` in
+it's definitions.
 
 .. _facet: http://en.wikipedia.org/wiki/Faceted_browser
 .. _filters: http://www.cubicweb.org/blogentry/154152
 .. _jquery: http://www.jqueryui.com/
 
-To use **HasRelationFacet** on a reverse relation add ``role = 'object'`` in
-it's definitions.
--- a/doc/book/en/development/devweb/index.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/index.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,20 +1,21 @@
 Web development
 ===============
 
-In this chapter, we will describe the core api for web development in the *CubicWeb* framework.
+In this chapter, we will describe the core APIs for web development in
+the *CubicWeb* framework.
 
 .. toctree::
    :maxdepth: 2
 
-   request
    publisher
    controllers
-   property
+   request
+   views/index
    rtags
-   views/index
+   js
+   css
    form
    facets
+   internationalization
+   property
    httpcaching
-   js
-   css
-   internationalization
--- a/doc/book/en/development/devweb/publisher.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/publisher.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,4 +1,63 @@
+.. _publisher:
+
 Publisher
 ---------
 
-XXX cubicweb.web.application; coop diagram for execution of a http query
\ No newline at end of file
+What happens when an HTTP request is issued ?
+
+The story begins with the ``CubicWebPublisher.main_publish``
+method. We do not get upper in the bootstrap process because it is
+dependant on the used HTTP library. With `twisted`_ however,
+``cubicweb.etwist.server.CubicWebRootResource.render_request`` is the
+real entry point.
+
+What main_publish does:
+
+* get a controller id and a result set from the path (this is actually
+  delegated to the `urlpublisher` component)
+
+* the controller is then selected (if not, this is considered an
+  authorization failure and signaled as sich) and called
+
+* then either a proper result is returned, in which case the
+  request/connection object issues a ``commit`` and returns the result
+
+* or error handling must happen:
+
+  * ``ValidationErrors`` pop up there and may lead to a redirect to a
+    previously arranged url or standard error handling applies
+  * an HTTP 500 error (`Internal Server Error`) is issued
+
+
+Now, let's turn to the controller. There are many of them in
+:mod:`cubicweb.web.views.basecontrollers`. We can just follow the
+default `view` controller that is selected on a `view` path. See the
+:ref:`controllers` chapter for more information on controllers.
+
+The `View` controller's entry point is the `publish` method. It does
+the following:
+
+* compute the `main` view to be applied, using either the given result
+  set or building one from a user provided rql string (`rql` and `vid`
+  can be forced from the url GET parameters), that is:
+
+    * compute the `vid` using the result set and the schema (see
+      `cubicweb.web.views.vid_from_rst`)
+    * handle all error cases that could happen in this phase
+
+* do some cache management chores
+
+* select a main template (typically `TheMainTemplate`, see chapter
+  :ref:`templates`)
+
+* call it with the result set and the computed view.
+
+What happens next actually depends on the template and the view, but
+in general this is the rendering phase.
+
+
+CubicWebPublisher API
+`````````````````````
+
+.. autoclass:: cubicweb.web.application.CubicWebPublisher
+   :members:
--- a/doc/book/en/development/devweb/request.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/request.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,5 +1,8 @@
 The `Request` class (`cubicweb.web`)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------------------
+
+Overview
+````````
 
 A request instance is created when an HTTP request is sent to the web server.
 It contains informations such as form parameters, user authenticated, etc.
@@ -16,15 +19,16 @@
 
 But also:
 
-:Session data handling:
+* `Session data handling`
+
   * `session_data()`, returns a dictionary containing all the session data
   * `get_session_data(key, default=None)`, returns a value associated to the given
     key or the value `default` if the key is not defined
   * `set_session_data(key, value)`, assign a value to a key
   * `del_session_data(key)`,  suppress the value associated to a key
 
+*  `Cookies handling`
 
-:Cookies handling:
   * `get_cookie()`, returns a dictionary containing the value of the header
     HTTP 'Cookie'
   * `set_cookie(cookie, key, maxage=300)`, adds a header HTTP `Set-Cookie`,
@@ -33,12 +37,14 @@
     window)
   * `remove_cookie(cookie, key)`, forces a value to expire
 
-:URL handling:
+* `URL handling`
+
   * `url()`, returns the full URL of the HTTP request
   * `base_url()`, returns the root URL of the web application
   * `relative_path()`, returns the relative path of the request
 
-:And more...:
+* `And more...`
+
   * `set_content_type(content_type, filename=None)`, adds the header HTTP
     'Content-Type'
   * `get_header(header)`, returns the value associated to an arbitrary header
@@ -56,6 +62,18 @@
 most of the interface of `Request` is defined in the session associated
 to the client.
 
+API
+```
 
-XXX autoclass !
-XXX create_entity
+The elements we gave in overview for above are built in three layers,
+from ``cubicweb.req.RequestSessionBase``, ``cubicweb.dbapi.DBAPIRequest`` and
+``cubicweb.web.CubicWebRequestBase``.
+
+.. autoclass:: cubicweb.req.RequestSessionBase
+   :members:
+
+.. autoclass:: cubicweb.dbapi.DBAPIRequest
+   :members:
+
+.. autoclass:: cubicweb.web.request.CubicWebRequestBase
+   :members:
--- a/doc/book/en/development/devweb/views/basetemplates.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/basetemplates.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,176 +1,93 @@
 .. -*- coding: utf-8 -*-
 
+.. |cubicweb| replace:: *CubicWeb*
+
 .. _templates:
 
 Templates
 =========
 
-[WRITE ME]
+Templates are the entry point for the |cubicweb| view system. As seen
+in :ref:`views_base_class`, there are two kinds of views: the
+templatable and non-templatable.
+
+Non-templatable views are standalone. They are responsible for all the
+details such as setting a proper content type (or mime type), the
+proper document headers, namespaces, etc. Examples are pure xml views
+such as RSS or Semantic Web views (`SIOC`_, `DOAP`_, `FOAF`_, `Linked
+Data`_, etc.).
 
-* talk about main templates, etc.
+.. _`SIOC`: http://sioc-project.org/
+.. _`DOAP`: http://trac.usefulinc.com/doap
+.. _`FOAF`: http://www.foaf-project.org/
+.. _`Linked Data`: http://linkeddata.org/
+
+Templatable views are not concerned with such pesky details. They
+leave it to the template. Conversely, the template's main job is to:
+
+* set up the proper document header and content type
+* define the general layout of a document
+* invoke adequate views in the various sections of the document
 
 
+Look at :mod:`cubicweb.web.views.basetemplates` and you will find the
+base templates used to generate (X)HTML for your application. The most
+important template there is `TheMainTemplate`.
 
-Look at ``cubicweb/web/views/basetemplates.py`` and you will
-find the base templates used to generate HTML for your application.
+.. _the_main_template_layout:
+
+TheMainTemplate
+---------------
+
+Layout and sections
+```````````````````
 
 A page is composed as indicated on the schema below :
 
 .. image:: ../../../images/main_template.png
 
-In this section we will go through a couple of the primary templates
-you must be interested in, that is to say, the HTMLPageHeader,
-the HTMLPageFooter and the TheMainTemplate.
-
-
-HTMLPageHeader
---------------
-
-Customize header
-~~~~~~~~~~~~~~~~
+The sections dispatches specific views:
 
-Let's now move the search box in the header and remove the login form
-from the header. We'll show how to move it to the left column of the application.
+* `header`: the rendering of the header is delegated to the
+  `htmlheader` view, whose default implementation can be found in
+  ``basetemplates.py`` and which does the following things:
 
-Let's say we do not want anymore the login menu to be in the header, but we
-prefer it to be in the left column just below the logo. As the left column is
-rendered by ``TheMainTemplate``, we will show how to do it in TheMainTemplate_.
-
-First, to remove the login menu, we just need to comment out the display of the
-login component such as follows : ::
-
-  class MyHTMLPageHeader(HTMLPageHeader):
+    * inject the favicon if there is one
+    * inject the global style sheets and javascript resources
+    * call and display a link to an rss component if there is one available
 
-      def main_header(self, view):
-          """build the top menu with authentification info and the rql box"""
-          self.w(u'<table id="header"><tr>\n')
-          self.w(u'<td id="firstcolumn">')
-          self._cw.vreg.select_component('logo', self._cw, self.cw_rset).dispatch(w=self.w)
-          self.w(u'</td>\n')
-          # appliname and breadcrumbs
-          self.w(u'<td id="headtext">')
-          comp = self._cw.vreg.select_component('appliname', self._cw, self.cw_rset)
-          if comp and comp.propval('visible'):
-              comp.dispatch(w=self.w)
-          comp = self._cw.vreg.select_component('breadcrumbs', self._cw, self.cw_rset, view=view)
-          if comp and comp.propval('visible'):
-              comp.dispatch(w=self.w, view=view)
-          self.w(u'</td>')
-          # logged user and help
-          #self.w(u'<td>\n')
-          #comp = self._cw.vreg.select_component('loggeduserlink', self._cw, self.cw_rset)
-          #comp.dispatch(w=self.w)
-          #self.w(u'</td><td>')
+  it also sets up the page title, and fills the actual
+  `header` section with top-level components, using the `header` view, which:
 
-          self.w(u'<td>')
-          helpcomp = self._cw.vreg.select_component('help', self._cw, self.cw_rset)
-          if helpcomp: # may not be available if Card is not defined in the schema
-              helpcomp.dispatch(w=self.w)
-          self.w(u'</td>')
-          # lastcolumn
-          self.w(u'<td id="lastcolumn">')
-          self.w(u'</td>\n')
-          self.w(u'</tr></table>\n')
-          self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden',
-                        title=False, message=False)
+    * tries to display a logo, the name of the application and the `breadcrumbs`
+    * provides a login status area
+    * provides a login box (hiden by default)
 
-
-
-.. image:: ../../../images/lax-book.06-header-no-login.en.png
-
-Let's now move the search box in the top-right header area. To do so, we will
-first create a method to get the search box display and insert it in the header
-table.
-
-::
+* `left column`: this is filled with all selectable boxes matching the
+  `left` context (there is also a right column but nowadays it is
+  seldom used due to bad usability)
 
- from cubicweb.web.views.basetemplates import HTMLPageHeader
- class MyHTMLPageHeader(HTMLPageHeader):
-    def main_header(self, view):
-        """build the top menu with authentification info and the rql box"""
-        self.w(u'<table id="header"><tr>\n')
-        self.w(u'<td id="firstcolumn">')
-        self._cw.vreg.select_component('logo', self._cw, self.cw_rset).dispatch(w=self.w)
-        self.w(u'</td>\n')
-        # appliname and breadcrumbs
-        self.w(u'<td id="headtext">')
-        comp = self._cw.vreg.select_component('appliname', self._cw, self.cw_rset)
-        if comp and comp.propval('visible'):
-            comp.dispatch(w=self.w)
-        comp = self._cw.vreg.select_component('breadcrumbs', self._cw, self.cw_rset, view=view)
-        if comp and comp.propval('visible'):
-            comp.dispatch(w=self.w, view=view)
-        self.w(u'</td>')
-
-        # logged user and help
-        #self.w(u'<td>\n')
-        #comp = self._cw.vreg.select_component('loggeduserlink', self._cw, self.cw_rset)
-        #comp.dispatch(w=self.w)
-        #self.w(u'</td><td>')
-
-        # search box
-        self.w(u'<td>')
-        self.get_searchbox(view, 'left')
-        self.w(u'</td>')
-
-        self.w(u'<td>')
-        helpcomp = self._cw.vreg.select_component('help', self._cw, self.cw_rset)
-        if helpcomp: # may not be available if Card is not defined in the schema
-            helpcomp.dispatch(w=self.w)
-        self.w(u'</td>')
-        # lastcolumn
-        self.w(u'<td id="lastcolumn">')
-        self.w(u'</td>\n')
-        self.w(u'</tr></table>\n')
-        self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden',
-                      title=False, message=False)
+* `contentcol`: this is the central column; it is filled with:
 
-    def get_searchbox(self, view, context):
-        boxes = list(self._cw.vreg.poss_visible_objects('boxes', self._cw, self.cw_rset,
-                                                    view=view, context=context))
-        if boxes:
-            for box in boxes:
-                if box.__regid__ == 'search_box':
-                    box.dispatch(w=self.w, view=view)
-
-
-
-
-HTMLPageFooter
---------------
-
-If you want to change the footer for example, look
-for HTMLPageFooter and override it in your views file as in :
-::
+    * the `rqlinput` view (hidden by default)
+    * the `applmessages` component
+    * the `contentheader` view which in turns dispatches all available
+      content navigation components having the `navtop` context (this
+      is used to navigate through entities implementing the IPrevNext
+      interface)
+    * the view that was given as input to the template's `call`
+      method, also dealing with pagination concerns
+    * the `contentfooter`
 
-  form cubicweb.web.views.basetemplates import HTMLPageFooter
-  class MyHTMLPageFooter(HTMLPageFooter):
-      def call(self, **kwargs):
-          self.w(u'<div class="footer">')
-          self.w(u'This website has been created with <a href="http://cubicweb.org">CubicWeb</a>.')
-          self.w(u'</div>')
+* `footer`: adds all footer actions
 
-Updating a view does not require any restart of the server. By reloading
-the page you can see your new page footer.
-
+.. note::
 
-TheMainTemplate
----------------
-.. _TheMainTemplate:
-
-TheMainTemplate is responsible for the general layout of the entire application.
-It defines the template of ``__regid__ = main`` that is used by the instance.
+  How and why a view object is given to the main template is explained
+  in the :ref:`publisher` chapter.
 
-The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`)
-builds the page based on the following pattern:
-
-.. image:: ../../../images/main_template_layout.png
-
-The rectangle containing `view.dispatch()` represents the area where the content
-view has to be displayed. The others represents sub-templates called to complete
-the page. A default implementation of those is provided in
-`cubicweb.views.basetemplates`. You can, of course, overload those sub-templates
-to implement your own customization of the HTML page.
+Class attributes
+````````````````
 
 We can also control certain aspects of the main template thanks to the following
 forms parameters:
@@ -184,6 +101,13 @@
   the dictionary of the forms parameters, before going the classic way (through
   step 1 and 2 described juste above)
 
-The MainTemplate is a bit complex as it tries to accomodate many
-different cases. We are now about to go through it and cutomize entirely
-our application.
+Other templates
+---------------
+
+Other standard templates include:
+
+* `login` and `logout`
+
+* `error-template` specializes TheMainTemplate to do proper end-user
+  output if an error occurs during the computation of TheMainTemplate
+  (it is a fallback view).
--- a/doc/book/en/development/devweb/views/boxes.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/boxes.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,5 +1,7 @@
-Boxes (:mod:`cubicweb.web.views.boxes`)
----------------------------------------------------------------
+Boxes
+-----
+
+(:mod:`cubicweb.web.views.boxes`)
 
 *sidebox*
   This view displays usually a side box of some related entities
@@ -24,7 +26,6 @@
   entity and link to it automatically
 
 
-
 If necessary, it is possible to overwrite the method
 `relation_mode(rtype, targettype, x='subject')` to dynamically
 compute a relation creation category.
--- a/doc/book/en/development/devweb/views/index.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/index.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,5 +1,5 @@
-The views system
-================
+The View system
+===============
 
 .. |cubicweb| replace:: *CubicWeb*
 
@@ -20,11 +20,15 @@
    table
    xmlrss
    autoform
-   editforms
+..   editforms
+
+.. toctree::
+   :maxdepth: 3
+
    urlpublish
    breadcrumbs
-   facets
-   wdoc
-   embedding
-   idownloadable
+..   facets
+..   wdoc
+..   embedding
+..   idownloadable
 
--- a/doc/book/en/development/devweb/views/startup.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/startup.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,5 +1,8 @@
-Startup views (:mod:`cubicweb.web.views.startup`)
--------------------------------------------------
+Startup views
+-------------
+
+ (:mod:`cubicweb.web.views.startup`)
+
 Usual selector: no_rset or yes.
 
 Views that don't apply to a result set
--- a/doc/book/en/development/devweb/views/urlpublish.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/urlpublish.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -1,7 +1,27 @@
 .. -*- coding: utf-8 -*-
 
-URL Rewriting (:mod:`cubicweb.web.views.urlpublish`) and (:mod:`cubicweb.web.views.urlrewrite`)
-------------------------------------------------------------------------------------------------
+URL publishing
+--------------
+
+(:mod:`cubicweb.web.views.urlpublishing`)
+
+.. automodule:: cubicweb.web.views.urlpublishing
+
+.. autoclass:: cubicweb.web.views.urlpublishing.URLPublisherComponent
+   :members:
+
+URL rewriting
+-------------
 
-XXX feed me
-show how urls are mapped to selections and views and explain URLRewriting
+(:mod:`cubicweb.web.views.urlrewrite`)
+
+.. autoclass:: cubicweb.web.views.urlrewrite.URLRewriter
+   :members:
+
+.. autoclass:: cubicweb.web.views.urlrewrite.SimpleReqRewriter
+   :members:
+
+.. autoclass:: cubicweb.web.views.urlrewrite.SchemaBasedRewriter
+   :members:
+
+
--- a/doc/book/en/development/devweb/views/views.rst	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/development/devweb/views/views.rst	Thu Apr 15 19:39:55 2010 +0200
@@ -13,6 +13,8 @@
 XHTML stream, but there are views concerned with email other non-html
 outputs.
 
+.. _views_base_class:
+
 Basic class for views
 ~~~~~~~~~~~~~~~~~~~~~
 
Binary file doc/book/en/images/facet_date_range.png has changed
Binary file doc/book/en/images/facet_has_image.png has changed
Binary file doc/book/en/images/facet_overview.png has changed
Binary file doc/book/en/images/facet_range.png has changed
--- a/doc/book/en/images/primaryview_template.svg	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/images/primaryview_template.svg	Thu Apr 15 19:39:55 2010 +0200
@@ -64,7 +64,10 @@
      transform="translate(162.2968,90.697922)">
     <g
        id="g3869"
-       transform="matrix(1,0,0,1.0373644,0,-72.039777)">
+       transform="matrix(1,0,0,1.0373644,0,-72.039777)"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449">
       <rect
          y="-15.840891"
          x="-159.08963"
@@ -89,14 +92,20 @@
        width="772.32111"
        height="43.888428"
        x="-131.1837"
-       y="86.559296" />
+       y="86.559296"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
        x="-122.69418"
        y="115.50363"
        id="text2385"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="-122.69418"
          y="115.50363"
@@ -107,14 +116,20 @@
        width="770.26868"
        height="203.16078"
        x="-125.88269"
-       y="172.90417" />
+       y="172.90417"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
        x="348.26724"
        y="205.34305"
        id="text3169"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="348.26724"
          y="205.34305"
@@ -125,14 +140,20 @@
        width="769.93549"
        height="237.84663"
        x="-125.03326"
-       y="391.32156" />
+       y="391.32156"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
        x="360.99954"
        y="428.38055"
        id="text3175"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="360.99954"
          y="428.38055"
@@ -143,7 +164,10 @@
        width="178.93939"
        height="612.36584"
        x="667.10443"
-       y="84.64225" />
+       y="84.64225"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:22px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.50000000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"
@@ -151,7 +175,10 @@
        y="-810.65997"
        id="text3187"
        transform="matrix(0,1,-1,0,0,0)"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          id="tspan2408">render_side_boxes()</tspan></text>
     <rect
@@ -160,14 +187,20 @@
        width="771.97766"
        height="55.647793"
        x="-127.80586"
-       y="642.0293" />
+       y="642.0293"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
        x="-121.22153"
        y="674.1748"
        id="text3181"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="-121.22153"
          y="674.1748"
@@ -178,14 +211,20 @@
        width="986.90503"
        height="45.800392"
        x="-128.34428"
-       y="-31.574066" />
+       y="-31.574066"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
        x="355.60541"
        y="-2.7424495"
        id="text3883"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="355.60541"
          y="-2.7424495"
@@ -196,14 +235,20 @@
        width="986.90503"
        height="45.800392"
        x="-128.87863"
-       y="19.723684" />
+       y="19.723684"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449" />
     <text
        xml:space="preserve"
        style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
        x="565.71027"
        y="50.135612"
        id="text3892"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          x="565.71027"
          y="50.135612"
@@ -214,7 +259,10 @@
        x="87.154541"
        y="114.2578"
        id="text3899"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          id="tspan3903"
          x="87.154541"
@@ -225,7 +273,10 @@
        x="88.46772"
        y="675.71582"
        id="text2410"
-       sodipodi:linespacing="125%"><tspan
+       sodipodi:linespacing="125%"
+       inkscape:export-filename="/home/auc/src/fcw/cubicweb/doc/book/en/images/primaryview_template.png"
+       inkscape:export-xdpi="60.912449"
+       inkscape:export-ydpi="60.912449"><tspan
          sodipodi:role="line"
          id="tspan2412"
          x="88.46772"
--- a/doc/book/en/standard_theme/static/lglb-sphinx-doc.css	Thu Apr 15 18:54:13 2010 +0200
+++ b/doc/book/en/standard_theme/static/lglb-sphinx-doc.css	Thu Apr 15 19:39:55 2010 +0200
@@ -10,10 +10,9 @@
 /* -- page layout ----------------------------------------------------------- */
 
 body {
-    font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+    font-family: 'Bitstream Vera Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
                  'Verdana', sans-serif;
     font-size: 14px;
-    letter-spacing: -0.01em;
     line-height: 150%;
     text-align: center;
     background-color: #D0D0D0;
--- a/i18n/fr.po	Thu Apr 15 18:54:13 2010 +0200
+++ b/i18n/fr.po	Thu Apr 15 19:39:55 2010 +0200
@@ -2212,7 +2212,7 @@
 
 #, python-format
 msgid "failed to uniquify path (%s, %s)"
-msgstr ""
+msgstr "ne peut obtenir un nom de fichier unique (%s, %s)"
 
 msgid "february"
 msgstr "février"
--- a/misc/migration/3.7.4_Any.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/misc/migration/3.7.4_Any.py	Thu Apr 15 19:39:55 2010 +0200
@@ -1,1 +1,5 @@
 sync_schema_props_perms('TrInfo', syncprops=False)
+
+config['http-session-time'] /= 60
+config['cleanup-session-time'] /= 60
+config['cleanup-anonymous-session-time'] /= 60
--- a/req.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/req.py	Thu Apr 15 19:39:55 2010 +0200
@@ -43,12 +43,10 @@
 
     request/session is the main resources accessor, mainly through it's vreg
     attribute:
-    :vreg:
-      the instance's registry
-    :vreg.schema:
-      the instance's schema
-    :vreg.config:
-      the instance's configuration
+
+    :attribute vreg: the instance's registry
+    :attribute vreg.schema: the instance's schema
+    :attribute vreg.config: the instance's configuration
     """
     def __init__(self, vreg):
         self.vreg = vreg
@@ -140,8 +138,8 @@
             raise Unauthorized(self._('only select queries are authorized'))
 
     def get_cache(self, cachename):
-        """
-        NOTE: cachename should be dotted names as in :
+        """cachename should be dotted names as in :
+
         - cubicweb.mycache
         - cubes.blog.mycache
         - etc.
--- a/selectors.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/selectors.py	Thu Apr 15 19:39:55 2010 +0200
@@ -202,7 +202,7 @@
 SELECTOR_LOGGER = logging.getLogger('cubicweb.selectors')
 TRACED_OIDS = None
 
-def _trace_selector(cls, ret):
+def _trace_selector(cls, selector, args, ret):
     # /!\ lltrace decorates pure function or __call__ method, this
     #     means argument order may be different
     if isinstance(cls, Selector):
@@ -225,7 +225,7 @@
     def traced(cls, *args, **kwargs):
         ret = selector(cls, *args, **kwargs)
         if TRACED_OIDS is not None:
-            _trace_selector(cls, ret)
+            _trace_selector(cls, selector, args, ret)
         return ret
     traced.__name__ = selector.__name__
     traced.__doc__ = selector.__doc__
--- a/server/migractions.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/server/migractions.py	Thu Apr 15 19:39:55 2010 +0200
@@ -712,7 +712,14 @@
         execute = self._cw.execute
         ss.execschemarql(execute, eschema, ss.eschema2rql(eschema, groupmap))
         # add specializes relation if needed
-        self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
+        specialized = eschema.specializes()
+        if specialized:
+            try:
+                specialized.eid = instschema[specialized].eid
+            except KeyError:
+                raise Exception('trying to add entity type but parent type is '
+                                'not yet in the database schema')
+            self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
         # register entity's attributes
         for rschema, attrschema in eschema.attribute_definitions():
             # ignore those meta relations, they will be automatically added
@@ -975,7 +982,13 @@
                 if syncprops:
                     self._synchronize_eschema(etype, syncperms=syncperms)
                 else:
-                    self._synchronize_permissions(self.fs_schema[etype], etype.eid)
+                    try:
+                        fseschema = self.fs_schema[etype]
+                    except KeyError:
+                        # entity type in the repository schema but not anymore
+                        # on the fs schema
+                        continue
+                    self._synchronize_permissions(fseschema, etype.eid)
         if commit:
             self.commit()
 
--- a/server/repository.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/server/repository.py	Thu Apr 15 19:39:55 2010 +0200
@@ -1091,6 +1091,7 @@
                 entity.check()
             only_inline_rels, need_fti_update = True, False
             relations = []
+            source = self.source_from_eid(entity.eid, session)
             for attr in list(edited_attributes):
                 if attr == 'eid':
                     continue
@@ -1106,19 +1107,18 @@
                         previous_value = previous_value[0][0] # got a result set
                         if previous_value == entity[attr]:
                             previous_value = None
-                        else:
+                        elif source.should_call_hooks:
                             hm.call_hooks('before_delete_relation', session,
                                           eidfrom=entity.eid, rtype=attr,
                                           eidto=previous_value)
                     relations.append((attr, entity[attr], previous_value))
-                source = self.source_from_eid(entity.eid, session)
-                if source.should_call_hooks:
-                    # call hooks for inlined relations
-                    for attr, value, _ in relations:
-                        hm.call_hooks('before_add_relation', session,
-                                      eidfrom=entity.eid, rtype=attr, eidto=value)
-                    if not only_inline_rels:
-                        hm.call_hooks('before_update_entity', session, entity=entity)
+            if source.should_call_hooks:
+                # call hooks for inlined relations
+                for attr, value, _ in relations:
+                    hm.call_hooks('before_add_relation', session,
+                                  eidfrom=entity.eid, rtype=attr, eidto=value)
+                if not only_inline_rels:
+                    hm.call_hooks('before_update_entity', session, entity=entity)
             source.update_entity(session, entity)
             self.system_source.update_info(session, entity, need_fti_update)
             if source.should_call_hooks:
--- a/server/sources/rql2sql.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/server/sources/rql2sql.py	Thu Apr 15 19:39:55 2010 +0200
@@ -23,6 +23,12 @@
 Potential optimization information is collected by the querier, sql generation
 is done according to this information
 
+cross RDMS note : read `Comparison of different SQL implementations`_
+by Troels Arvin. Features SQL ISO Standard, PG, mysql, Oracle, MS SQL, DB2
+and Informix.
+
+.. _Comparison of different SQL implementations: http://www.troels.arvin.dk/db/rdbms 
+
 
 :organization: Logilab
 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
--- a/web/application.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/application.py	Thu Apr 15 19:39:55 2010 +0200
@@ -36,14 +36,15 @@
 
     def __init__(self, vreg):
         self.session_time = vreg.config['http-session-time'] or None
-        assert self.session_time is None or self.session_time > 0
-        self.cleanup_session_time = vreg.config['cleanup-session-time'] or 43200
-        assert self.cleanup_session_time > 0
-        self.cleanup_anon_session_time = vreg.config['cleanup-anonymous-session-time'] or 120
+        if self.session_time is not None:
+            assert self.session_time > 0
+            self.session_time *= 60 # convert minutes to seconds
+            self.cleanup_session_time = self.session_time
+        else:
+            self.cleanup_session_time = (vreg.config['cleanup-session-time'] or 1440) * 60
+            assert self.cleanup_session_time > 0
+        self.cleanup_anon_session_time = (vreg.config['cleanup-anonymous-session-time'] or 5) * 60
         assert self.cleanup_anon_session_time > 0
-        if self.session_time:
-            assert self.cleanup_session_time < self.session_time
-            assert self.cleanup_anon_session_time < self.session_time
         self.authmanager = vreg['components'].select('authmanager', vreg=vreg)
 
     def clean_sessions(self):
--- a/web/request.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/request.py	Thu Apr 15 19:39:55 2010 +0200
@@ -526,6 +526,7 @@
     def add_css(self, cssfiles, media=u'all', localfile=True, ieonly=False,
                 iespec=u'[if lt IE 8]'):
         """specify a CSS file to include in the HTML headers
+
         :param cssfiles: a CSS filename or a list of CSS filenames
         :param media: the CSS's media if necessary
         :param localfile: if True, the default data dir prefix is added to the
@@ -553,15 +554,17 @@
 
     def build_ajax_replace_url(self, nodeid, rql, vid, replacemode='replace',
                                **extraparams):
-        """builds an ajax url that will replace `nodeid`s content
+        """builds an ajax url that will replace nodeid's content
+
         :param nodeid: the dom id of the node to replace
         :param rql: rql to execute
         :param vid: the view to apply on the resultset
         :param replacemode: defines how the replacement should be done.
+
         Possible values are :
-         - 'replace' to replace the node's content with the generated HTML
-         - 'swap' to replace the node itself with the generated HTML
-         - 'append' to append the generated HTML to the node's content
+        - 'replace' to replace the node's content with the generated HTML
+        - 'swap' to replace the node itself with the generated HTML
+        - 'append' to append the generated HTML to the node's content
         """
         url = self.build_url('view', rql=rql, vid=vid, __notemplate=1,
                              **extraparams)
--- a/web/test/unittest_breadcrumbs.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/test/unittest_breadcrumbs.py	Thu Apr 15 19:39:55 2010 +0200
@@ -15,7 +15,7 @@
         ibc = self.vreg['components'].select('breadcrumbs', self.request(), rset=childrset)
         self.assertEquals(ibc.render(),
                           """<span id="breadcrumbs" class="pathbar">&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/Folder">folder_plural</a>&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/folder/%s" title="">par&amp;ent</a>&#160;&gt;&#160;
-chi&amp;ld</span>""" % f1.eid)
+<a href="http://testing.fr/cubicweb/folder/%s" title="">chi&amp;ld</a></span>""" % (f1.eid, f2.eid))
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/web/views/facets.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/views/facets.py	Thu Apr 15 19:39:55 2010 +0200
@@ -69,7 +69,7 @@
         rset, vid, divid, paginate = self._get_context(view)
         if rset.rowcount < 2: # XXX done by selectors, though maybe necessary when rset has been hijacked
             return
-        rqlst = self.cw_rset.syntax_tree()
+        rqlst = rset.syntax_tree()
         # union not yet supported
         if len(rqlst.children) != 1:
             return ()
--- a/web/views/ibreadcrumbs.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/views/ibreadcrumbs.py	Thu Apr 15 19:39:55 2010 +0200
@@ -62,10 +62,7 @@
 
     def wpath_part(self, part, contextentity, last=False):
         if isinstance(part, Entity):
-            if last and part.eid == contextentity.eid:
-                self.w(xml_escape(part.view('breadcrumbtext')))
-            else:
-                self.w(part.view('breadcrumbs'))
+            self.w(part.view('breadcrumbs'))
         elif isinstance(part, tuple):
             url, title = part
             textsize = self._cw.property_value('navigation.short-line-size')
--- a/web/views/urlpublishing.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/views/urlpublishing.py	Thu Apr 15 19:39:55 2010 +0200
@@ -1,28 +1,26 @@
-"""associate url's path to view identifier / rql queries
+# 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
+"""Associate url's path to view identifier / rql queries.
 
-It currently handle url's path with the forms
+It currently handles url path with the forms:
 
 * <publishing_method>
+* minimal REST publishing:
 
-* minimal REST publishing:
   * <eid>
   * <etype>[/<attribute name>/<attribute value>]*
-
 * folder navigation
 
-
-You can actually control URL (more exactly path) resolution using URL path
-evaluator.
-
-XXX actionpath and folderpath execute a query whose results is lost
-because of redirecting instead of direct traversal
+You can actually control URL (more exactly path) resolution using an
+URL path evaluator.
 
-: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
+.. note::
+
+ Actionpath and Folderpath execute a query whose results is lost
+ because of redirecting instead of direct traversal.
 """
-
 __docformat__ = "restructuredtext en"
 
 from rql import TypeResolverException
@@ -37,17 +35,18 @@
     """
 
 class URLPublisherComponent(component.Component):
-    """associate url's path to view identifier / rql queries,
-    by applying a chain of urlpathevaluator components.
+    """Associate url path to view identifier / rql queries, by
+    applying a chain of urlpathevaluator components.
 
-    An evaluator is a URLPathEvaluator subclass with a .evaluate_path
+    An evaluator is a URLPathEvaluator subclass with an .evaluate_path
     method taking the request object and the path to publish as
-    argument.  It will either returns a publishing method identifier
-    and a rql query on success or raises a `PathDontMatch` exception
-    on failure. URL evaluators are called according to their `priority`
-    attribute, with 0 as the greatest priority and greater values as
-    lower priority.  The first evaluator returning a result or raising
-    something else than `PathDontMatch` will stop the handlers chain.
+    argument.  It will either return a publishing method identifier
+    and an rql query on success or raise a `PathDontMatch` exception
+    on failure. URL evaluators are called according to their
+    `priority` attribute, with 0 as the greatest priority and greater
+    values as lower priority. The first evaluator returning a result
+    or raising something else than `PathDontMatch` will stop the
+    handlers chain.
     """
     __regid__ = 'urlpublisher'
     vreg = None # XXX necessary until property for deprecation warning is on appobject
@@ -64,18 +63,18 @@
         self.evaluators = sorted(evaluators, key=lambda x: x.priority)
 
     def process(self, req, path):
-        """given an url (essentialy caracterized by a path on the server,
-        but additional information may be found in the request object), return
-        a publishing method identifier (eg controller) and an optional result
-        set
+        """Given an url (essentialy caracterized by a path on the
+        server, but additional information may be found in the request
+        object), return a publishing method identifier
+        (e.g. controller) and an optional result set.
 
-        :type req: `cubicweb.web.Request`
+        :type req: `cubicweb.web.request.CubicWebRequestBase`
         :param req: the request object
 
         :type path: str
         :param path: the path of the resource to publish
 
-        :rtype: tuple(str, `cubicweb.utils.ResultSet` or None)
+        :rtype: tuple(str, `cubicweb.rset.ResultSet` or None)
         :return: the publishing method identifier and an optional result set
 
         :raise NotFound: if no handler is able to decode the given path
--- a/web/views/urlrewrite.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/views/urlrewrite.py	Thu Apr 15 19:39:55 2010 +0200
@@ -1,4 +1,4 @@
-"""Rules based url rewriter component, to get configurable RESTful urls
+"""Rules based url rewriter component, to get configurable RESTful urls.
 
 :organization: Logilab
 :copyright: 2007-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -40,17 +40,17 @@
 
 
 class URLRewriter(AppObject):
-    """base class for URL rewriters
+    """Base class for URL rewriters.
 
-    url rewriters should have a `rules` dict that maps an input URI
+    Url rewriters should have a `rules` dict that maps an input URI
     to something that should be used for rewriting.
 
     The actual logic that defines how the rules dict is used is implemented
-    in the `rewrite` method
+    in the `rewrite` method.
 
     A `priority` attribute might be used to indicate which rewriter
     should be tried first. The higher the priority is, the earlier the
-    rewriter will be tried
+    rewriter will be tried.
     """
     __metaclass__ = metarewriter
     __registry__ = 'urlrewriting'
@@ -62,11 +62,11 @@
 
 
 class SimpleReqRewriter(URLRewriter):
-    """The SimpleReqRewriters uses a `rules` dict that maps
-    input URI (regexp or plain string) to a dictionary to update the
-    request's form
+    """The SimpleReqRewriters uses a `rules` dict that maps input URI
+    (regexp or plain string) to a dictionary to update the request's
+    form.
 
-    If the input uri is a regexp, group substitution is allowed
+    If the input uri is a regexp, group substitution is allowed.
     """
     __regid__ = 'simple'
 
--- a/web/webconfig.py	Thu Apr 15 18:54:13 2010 +0200
+++ b/web/webconfig.py	Thu Apr 15 19:39:55 2010 +0200
@@ -115,27 +115,28 @@
         ('http-session-time',
          {'type' : 'int',
           'default': 0,
-          'help': 'duration in seconds for HTTP sessions. 0 mean no expiration. '\
-          'Should be greater than RQL server\'s session-time.',
+          'help': "duration in minutes of the cookie used to store session "
+          "identifier. If 0, the cookie will expire when the user exist its "
+          "browser. Should be 0 or greater than repository\'s session-time.",
           'group': 'web', 'inputlevel': 2,
           }),
         ('cleanup-session-time',
          {'type' : 'int',
-          'default': 43200,
-          'help': 'duration in seconds for which unused connections should be '\
-          'closed, to limit memory consumption. This is different from '\
-          'http-session-time since in some cases you may have an unexpired http '\
-          'session (e.g. valid session cookie) which will trigger transparent '\
-          'creation of a new session. In other cases, sessions may never expire \
-          and cause memory leak. Should be smaller than http-session-time, '\
-          'unless it\'s 0. Default to 12 h.',
+          'default': 1440,
+          'help': 'duration of inactivity in minutes after which a connection '
+          'will be closed, to limit memory consumption (avoid sessions that '
+          'never expire and cause memory leak when http-session-time is 0). '
+          'So even if http-session-time is 0 and the user don\'t close his '
+          'browser, he will have to reauthenticate after this time of '
+          'inactivity. Default to 24h.',
           'group': 'web', 'inputlevel': 2,
           }),
         ('cleanup-anonymous-session-time',
          {'type' : 'int',
-          'default': 120,
-          'help': 'Same as cleanup-session-time but specific to anonymous '\
-          'sessions. Default to 2 min.',
+          'default': 5,
+          'help': 'Same as cleanup-session-time but specific to anonymous '
+          'sessions. You can have a much smaller timeout here since it will be '
+          'transparent to the user. Default to 5min.',
           'group': 'web', 'inputlevel': 2,
           }),
         ('force-html-content-type',