--- 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"> > <a href="http://testing.fr/cubicweb/Folder">folder_plural</a> > <a href="http://testing.fr/cubicweb/folder/%s" title="">par&ent</a> > 
-chi&ld</span>""" % f1.eid)
+<a href="http://testing.fr/cubicweb/folder/%s" title="">chi&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',