# HG changeset patch # User Sylvain Thénault # Date 1271328520 -7200 # Node ID 16461f6757344397f1e2f783b26c54a06677b043 # Parent f7d2df59231ad59944800dbc98daa2c1374d02dd# Parent c4caef6f09c9aa827c2b0843300efeab50beda99 backport stable diff -r f7d2df59231a -r 16461f675734 _exceptions.py --- a/_exceptions.py Thu Apr 15 12:47:02 2010 +0200 +++ b/_exceptions.py Thu Apr 15 12:48:40 2010 +0200 @@ -122,8 +122,6 @@ class UnknownProperty(RegistryException): """property found in database but unknown in registry""" -class RegistryOutOfDate(RegistryException): - """raised when a source file modification is detected""" # query exception ############################################################# diff -r f7d2df59231a -r 16461f675734 cwconfig.py --- a/cwconfig.py Thu Apr 15 12:47:02 2010 +0200 +++ b/cwconfig.py Thu Apr 15 12:48:40 2010 +0200 @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -#:organization: Logilab -#:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. -#:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -#:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses - -# docstring included in doc/book/en/admin/setup.rst +# organization: Logilab +# copyright 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. +# contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +# Licensed under the GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ .. _ResourceMode: @@ -13,7 +11,7 @@ A resource *mode* is a predifined set of settings for various resources directories, such as cubes, instances, etc. to ease development with the -framework. There are two running modes with |cubicweb|: +framework. There are two running modes with *CubicWeb*: * 'user', resources are searched / created in the user home directory: @@ -80,7 +78,7 @@ Python `````` -If you installed |cubicweb| by cloning the Mercurial forest or from source +If you installed *CubicWeb* by cloning the Mercurial forest or from source distribution, then you will need to update the environment variable PYTHONPATH by adding the path to the forest `cubicweb`: @@ -89,7 +87,7 @@ export PYTHONPATH=/full/path/to/cubicweb-forest -If you installed |cubicweb| with packages, no configuration is required and your +If you installed *CubicWeb* with packages, no configuration is required and your new cubes will be placed in `/usr/share/cubicweb/cubes` and your instances will be placed in `/etc/cubicweb.d`. @@ -97,11 +95,11 @@ CubicWeb ```````` -Here are all environment variables that may be used to configure |cubicweb|: +Here are all environment variables that may be used to configure *CubicWeb*: .. envvar:: CW_MODE - Resource mode: user or system, as explained in :ref:ResourceMode. + Resource mode: user or system, as explained in :ref:`ResourceMode`. .. envvar:: CW_CUBES_PATH diff -r f7d2df59231a -r 16461f675734 cwvreg.py --- a/cwvreg.py Thu Apr 15 12:47:02 2010 +0200 +++ b/cwvreg.py Thu Apr 15 12:48:40 2010 +0200 @@ -185,7 +185,7 @@ from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid, ObjectNotFound, NoSelectableObject, RegistryNotFound, - RegistryOutOfDate, CW_EVENT_MANAGER, onevent) + CW_EVENT_MANAGER, onevent) from cubicweb.utils import dump_class from cubicweb.vregistry import VRegistry, Registry, class_regid from cubicweb.rtags import RTAGS @@ -460,8 +460,8 @@ def itervalues(self): return (value for key, value in self.items()) - def reset(self, path=None, force_reload=None): - super(CubicWebVRegistry, self).reset(path, force_reload) + def reset(self): + super(CubicWebVRegistry, self).reset() self._needs_iface = {} # two special registries, propertydefs which care all the property # definitions, and propertyvals which contains values for those @@ -471,23 +471,12 @@ self['propertyvalues'] = self.eprop_values = {} for key, propdef in self.config.eproperty_definitions(): self.register_property(key, **propdef) - if path is not None and 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) def set_schema(self, schema): """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(), force_reload=False) + self.reload(self.config.vregistry_path()) # map lowered entity type names to their actual name self.case_insensitive_etypes = {} for eschema in self.schema.entities(): @@ -496,12 +485,26 @@ clear_cache(eschema, 'ordered_relations') clear_cache(eschema, 'meta_attributes') - def _reload(self, path, force_reload): + def reload_if_needed(self): + path = self.config.vregistry_path() + if self.is_reload_needed(path): + self.reload(path) + + def reload(self, path): + """modification detected, reset and reload the vreg""" CW_EVENT_MANAGER.emit('before-registry-reload') - # modification detected, reset and reload - self.reset(path, force_reload) - super(CubicWebVRegistry, self).register_objects( - path, force_reload, self.config.extrapath) + 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) CW_EVENT_MANAGER.emit('after-registry-reload') def _set_schema(self, schema): @@ -546,15 +549,10 @@ if ifaces: self._needs_iface[obj] = ifaces - def register_objects(self, path, force_reload=None): + def register_objects(self, path, force_reload=False): """overriden to remove objects requiring a missing interface""" - if force_reload is None: - force_reload = self.config.debugmode - try: - super(CubicWebVRegistry, self).register_objects( - path, force_reload, self.config.extrapath) - except RegistryOutOfDate: - self._reload(path, force_reload) + super(CubicWebVRegistry, self).register_objects( + path, force_reload, self.config.extrapath) def initialization_completed(self): """cw specific code once vreg initialization is completed: diff -r f7d2df59231a -r 16461f675734 devtools/__init__.py --- a/devtools/__init__.py Thu Apr 15 12:47:02 2010 +0200 +++ b/devtools/__init__.py Thu Apr 15 12:48:40 2010 +0200 @@ -214,8 +214,10 @@ driver = config.sources()['system']['db-driver'] if driver == 'sqlite': reset_test_database_sqlite(config) - elif driver == 'sqlserver2005': - reset_test_database_sqlserver2005(config) + elif driver in ('sqlserver2005', 'postgres'): + # XXX do something with dump/restore ? + print 'resetting the database is not done for', driver + print 'you should handle it manually' else: raise ValueError('no reset function for driver %r' % driver) @@ -236,9 +238,6 @@ from cubicweb.server import init_repository init_repository(config, interactive=False, drop=True, vreg=vreg) -def reset_test_database_sqlserver2005(config): - pass - ### sqlite test database handling ############################################## def cleanup_sqlite(dbfile, removetemplate=False): diff -r f7d2df59231a -r 16461f675734 devtools/testlib.py --- a/devtools/testlib.py Thu Apr 15 12:47:02 2010 +0200 +++ b/devtools/testlib.py Thu Apr 15 12:48:40 2010 +0200 @@ -134,6 +134,7 @@ class CubicWebTC(TestCase): """abstract class for test using an apptest environment + attributes: * `vreg`, the vregistry @@ -145,7 +146,6 @@ * `repo`, the repository object * `admlogin`, login of the admin user * `admpassword`, password of the admin user - """ appid = 'data' configcls = devtools.ApptestConfiguration diff -r f7d2df59231a -r 16461f675734 doc/book/en/admin/index.rst --- a/doc/book/en/admin/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/admin/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -2,9 +2,9 @@ .. _Part3: -------------------------- -Part III - Administration -------------------------- +-------------- +Administration +-------------- This part is for installation and administration of the *CubicWeb* framework and instances based on that framework. diff -r f7d2df59231a -r 16461f675734 doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/admin/setup.rst Thu Apr 15 12:48:40 2010 +0200 @@ -141,6 +141,15 @@ Please be careful to select the right python (2.5) and postgres (8.4) versions. +A windows compiled recent version of gettext:: + + http://ftp.logilab.org/pub/gettext/gettext-0.17-win32-setup.exe + +A pre-compiled version of rql for windows (take care of retrieving the +most recent version available there):: + + http://ftp.logilab.org/pub/rql/rql-0.23.0.win32-py2.5.exe + Pyro enables remote access to cubicweb repository instances. Get it there:: http://sourceforge.net/projects/pyro/files/ diff -r f7d2df59231a -r 16461f675734 doc/book/en/annexes/index.rst --- a/doc/book/en/annexes/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/annexes/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -2,9 +2,9 @@ .. _Part4: --------------------- -Part IV - Appendixes --------------------- +---------- +Appendixes +---------- The following chapters are reference material. diff -r f7d2df59231a -r 16461f675734 doc/book/en/conf.py --- a/doc/book/en/conf.py Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/conf.py Thu Apr 15 12:48:40 2010 +0200 @@ -32,10 +32,10 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'logilab.common.sphinx_ext']#, 'sphinxcontrib.aafig'] +extensions = ['sphinx.ext.autodoc', 'logilab.common.sphinx_ext'] autoclass_content = 'both' # Add any paths that contain templates here, relative to this directory. -templates_path = ['.templates'] +#templates_path = [] # The suffix of source filenames. source_suffix = '.rst' @@ -97,8 +97,8 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = '%s %s' % (project, release) -html_theme = 'lglb_doc' -html_theme_path = ['_theme'] +html_theme = 'standard_theme' +html_theme_path = ['.'] # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None @@ -189,4 +189,3 @@ # If false, no module index is generated. #latex_use_modindex = True #aafig_format = dict(latex='pdf', html='svg', text=None) - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devcore/cwconfig.rst --- a/doc/book/en/development/devcore/cwconfig.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devcore/cwconfig.rst Thu Apr 15 12:48:40 2010 +0200 @@ -1,5 +1,5 @@ -:mod:`Configuration ` ----------------------------------------- +Configuration +------------- -.. .. automodule:: cubicweb.cwconfig -.. :members: +.. automodule:: cubicweb.cwconfig + :members: diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devcore/dbapi.rst --- a/doc/book/en/development/devcore/dbapi.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devcore/dbapi.rst Thu Apr 15 12:48:40 2010 +0200 @@ -8,6 +8,7 @@ The most important method is the `execute` method of a cursor. .. sourcecode:: python + execute(rqlstring, args=None, cachekey=None, build_descr=True) :rqlstring: the RQL query to execute (unicode) diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devcore/reqbase.rst --- a/doc/book/en/development/devcore/reqbase.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devcore/reqbase.rst Thu Apr 15 12:48:40 2010 +0200 @@ -1,3 +1,5 @@ +Request and ResultSet methods +----------------------------- Those are methods you'll find on both request objects and on repository session: diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devrepo/hooks.rst --- a/doc/book/en/development/devrepo/hooks.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devrepo/hooks.rst Thu Apr 15 12:48:40 2010 +0200 @@ -320,6 +320,11 @@ * when several hooks need to instantiate the same operation (e.g. an entity and a relation hook). +.. note:: + + A more realistic example can be found in the advanced tutorial + chapter :ref:`adv_tuto_security_propagation`. + Operation: a small API overview ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/controllers.rst --- a/doc/book/en/development/devweb/controllers.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devweb/controllers.rst Thu Apr 15 12:48:40 2010 +0200 @@ -1,3 +1,5 @@ +.. _controllers: + Controllers ----------- @@ -10,7 +12,7 @@ The following controllers are provided out-of-the box in CubicWeb. We list them by category. -Browsing: +`Browsing`: * the View controller (web/views/basecontrollers.py) is associated with most browsing actions within a CubicWeb application: it always @@ -27,9 +29,9 @@ * the Login/Logout controllers (web/views/basecontrollers.py) make effective user login or logout requests -Edition: +`Edition`: -* the Edit controller (web/views/editcontroller.py) handles CRUD +* the Edit controller (see :ref:`edit_controller`) handles CRUD operations in response to a form being submitted; it works in close association with the Forms, to which it delegates some of the work @@ -40,7 +42,7 @@ Form validator controller, and the UI is decorated with failure information, either global or per-field , until it is valid) -Other: +`Other`: * the SendMail controller (web/views/basecontrollers.py) is reponsible for outgoing email notifications @@ -76,4 +78,122 @@ process. +.. _edit_controller: +The `edit controller` ++++++++++++++++++++++ + +It can be found in (:mod:`cubicweb.web.views.editcontroller`). + +Editing control +~~~~~~~~~~~~~~~~ + +Re-requisites: the parameters related to entities to edit are +specified as follows :: + + : + +where entity eid could be a letter in case of an entity to create. We +name those parameters as *qualified*. + +1. Retrieval of entities to edit by looking for the forms parameters + starting by `eid:` and also having a parameter `__type` associated + (also *qualified* by eid) + +2. For all the attributes and the relations of an entity to edit: + + 1. search for a parameter `edits-` or `edito-` + qualified in the case of a relation where the entity is object + 2. if found, the value returned is considered as the initial value + for this relaiton and we then look for the new value(s) in the parameter + (qualified) + 3. if the value returned is different from the initial value, an database update + request is done + +3. For each entity to edit: + + 1. if a qualified parameter `__linkto` is specified, its value has to be + a string (or a list of string) such as: :: + + :: + + where is either `subject` or `object` and each eid could be + separated from the others by a `_`. Target specifies if the *edited entity* + is subject or object of the relation and each relation specified will + be inserted. + + 2. if a qualified parameter `__clone_eid` is specified for an entity, the + relations of the specified entity passed as value of this parameter are + copied on the edited entity. + + 3. if a qualified parameter `__delete` is specified, its value must be + a string or a list of string such as follows: :: + + :: + + where each eid subject or object can be seperated from the other + by `_`. Each relation specified will be deleted. + + 4. if a qualified parameter `__insert` is specified, its value should + follow the same pattern as `__delete`, but each relation specified is + inserted. + +4. If the parameters `__insert` and/or `__delete` are found not qualified, + they are interpreted as explained above (independantly from the number + of entities edited). + +5. If no entity is edited but the form contains the parameters `__linkto` + and `eid`, this one is interpreted by using the value specified for `eid` + to designate the entity on which to add the relations. + + +.. note:: + + * If the parameter `__action_delete` is found, all the entities specified + as to be edited will be deleted. + + * If the parameter `__action_cancel` is found, no action is completed. + + * If the parameter `__action_apply` is found, the editing is + applied normally but the redirection is done on the form (see + :ref:`RedirectionControl`). + + * The parameter `__method` is also supported as for the main template + + * If no entity is found to be edited and if there is no parameter + `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or + `__insert`, an error is raised. + + * Using the parameter `__message` in the form will allow to use its value + as a message to provide the user once the editing is completed. + + +.. _RedirectionControl: + +Redirection control +~~~~~~~~~~~~~~~~~~~ +Once editing is completed, there is still an issue left: where should we go +now? If nothing is specified, the controller will do his job but it does not +mean we will be happy with the result. We can control that by using the +following parameters: + +* `__redirectpath`: path of the URL (relative to the root URL of the site, + no form parameters + +* `__redirectparams`: forms parameters to add to the path + +* `__redirectrql`: redirection RQL request + +* `__redirectvid`: redirection view identifier + +* `__errorurl`: initial form URL, used for redirecting in case a validation + error is raised during editing. If this one is not specified, an error page + is displayed instead of going back to the form (which is, if necessary, + responsible for displaying the errors) + +* `__form_id`: initial view form identifier, used if `__action_apply` is + found + +In general we use either `__redirectpath` and `__redirectparams` or +`__redirectrql` and `__redirectvid`. + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/index.rst --- a/doc/book/en/development/devweb/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devweb/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -4,14 +4,14 @@ In this chapter, we will describe the core api for web development in the *CubicWeb* framework. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 request publisher controllers property rtags - views + views/index form facets httpcaching diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/rtags.rst --- a/doc/book/en/development/devweb/rtags.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/devweb/rtags.rst Thu Apr 15 12:48:40 2010 +0200 @@ -1,14 +1,21 @@ Configuring the user interface ------------------------------ +.. _relation_tags: Relation tags ~~~~~~~~~~~~~ .. automodule:: cubicweb.rtags - +.. _uicfg_module: The ``uicfg`` module ~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + The part of uicfg that deals with primary views is in the + :ref:`primary_view_configuration` chapter. + .. automodule:: cubicweb.web.uicfg diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views.rst --- a/doc/book/en/development/devweb/views.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,267 +0,0 @@ - -.. _Views: - -Views ------ - -This chapter aims to describe the concept of a `view` used all along -the development of a web application and how it has been implemented -in *CubicWeb*. - -We'll start with a description of the interface providing you with a basic -understanding of the available classes and methods, then detail the view -selection principle which makes *CubicWeb* web interface very flexible. - -A `View` is an object applied to another object such as an entity. - -Basic class for views -~~~~~~~~~~~~~~~~~~~~~ - -Class `View` (`cubicweb.view`) -````````````````````````````````````` - -This class is an abstraction of a view class, used as a base class for every -renderable object such as views, templates, graphic components, etc. - -A `View` is instantiated to render a result set or part of a result set. `View` -subclasses may be parametrized using the following class attributes: - -* `templatable` indicates if the view may be embedded in a main - template or if it has to be rendered standalone (i.e. XML views must - not be embedded in the main template for HTML pages) - -* if the view is not templatable, it should set the `content_type` - class attribute to the correct MIME type (text/xhtml by default) - -* the `category` attribute may be used in the interface to regroup - related objects together - -A view writes to its output stream thanks to its attribute `w` (an -`UStreamIO`, except for binary views). - -At instantiation time, the standard `_cw` and `cw_rset` attributes are -added and the `w` attribute will be set at rendering time. - -The basic interface for views is as follows (remember that the result set has a -tabular structure with rows and columns, hence cells): - -* `render(**context)`, render the view by calling `call` or - `cell_call` depending on the given parameters - -* `call(**kwargs)`, call the view for a complete result set or null - (the default implementation calls `cell_call()` on each cell of the - result set) - -* `cell_call(row, col, **kwargs)`, call the view for a given cell of a - result set (`row` and `col` being integers used to access the cell) - -* `url()`, returns the URL enabling us to get the view with the current - result set - -* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of - identifier `__vid` on the given result set. It is possible to give a - fallback view identifier that will be used if the requested view is - not applicable to the result set. - -* `html_headers()`, returns a list of HTML headers to be set by the - main template - -* `page_title()`, returns the title to use in the HTML header `title` - -Other basic view classes -```````````````````````` -Here are some of the subclasses of `View` defined in `cubicweb.common.view` -that are more concrete as they relate to data rendering within the application: - -* `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) -* `StartupView`, start view that does not require a result set to apply to -* `AnyRsetView`, view applicable to any result set - -Examples of views class ------------------------ - -- Using `templatable`, `content_type` and HTTP cache configuration - -.. sourcecode:: python - - class RSSView(XMLView): - __regid__ = 'rss' - title = _('rss') - templatable = False - content_type = 'text/xml' - http_cache_manager = MaxAgeHTTPCacheManager - cache_max_age = 60*60*2 # stay in http cache for 2 hours by default - - -- Using custom selector - -.. sourcecode:: python - - class SearchForAssociationView(EntityView): - """view called by the edition view when the user asks - to search for something to link to the edited eid - """ - __regid__ = 'search-associate' - title = _('search for association') - __select__ = one_line_rset() & match_search_state('linksearch') & implements('Any') - - - -Example of view customization and creation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We'll show you now an example of a ``primary`` view and how to customize it. - -If you want to change the way a ``BlogEntry`` is displayed, just override -the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``: - -.. sourcecode:: python - - from cubicweb.selectors import implements - from cubicweb.web.views.primary improt Primaryview - - class BlogEntryPrimaryView(PrimaryView): - __select__ = PrimaryView.__select__ & implements('BlogEntry') - - def render_entity_attributes(self, entity): - self.w(u'

published on %s

' % - entity.publish_date.strftime('%Y-%m-%d')) - super(BlogEntryPrimaryView, self).render_entity_attributes(entity) - -The above source code defines a new primary view for -``BlogEntry``. The `id` class attribute is not repeated there since it -is inherited through the `primary.PrimaryView` class. - -The selector for this view chains the selector of the inherited class -with its own specific criterion. - -The view method ``self.w()`` is used to output data. Here `lines -08-09` output HTML for the publication date of the entry. - -.. image:: ../../images/lax-book.09-new-view-blogentry.en.png - :alt: blog entries now look much nicer - -Let us now improve the primary view of a blog - -.. sourcecode:: python - - from logilab.mtconverter import xml_escape - from cubicweb.selectors import implements, one_line_rset - from cubicweb.web.views.primary import Primaryview - - class BlogPrimaryView(PrimaryView): - __regid__ = 'primary' - __select__ = PrimaryView.__select__ & implements('Blog') - rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s' - - def render_entity_relations(self, entity): - rset = self._cw.execute(self.rql, {'b' : entity.eid}) - for entry in rset.entities(): - self.w(u'

%s

' % entry.view('inblogcontext')) - - class BlogEntryInBlogView(EntityView): - __regid__ = 'inblogcontext' - __select__ = implements('BlogEntry') - - def cell_call(self, row, col): - entity = self.cw_rset.get_entity(row, col) - self.w(u'%s' % - entity.absolute_url(), - xml_escape(entity.content[:50]), - xml_escape(entity.description)) - -This happens in two places. First we override the -render_entity_relations method of a Blog's primary view. Here we want -to display our blog entries in a custom way. - -At `line 10`, a simple request is made to build a result set with all -the entities linked to the current ``Blog`` entity by the relationship -``entry_of``. The part of the framework handling the request knows -about the schema and infers that such entities have to be of the -``BlogEntry`` kind and retrieves them (in the prescribed publish_date -order). - -The request returns a selection of data called a result set. Result -set objects have an .entities() method returning a generator on -requested entities (going transparently through the `ORM` layer). - -At `line 13` the view 'inblogcontext' is applied to each blog entry to -output HTML. (Note that the 'inblogcontext' view is not defined -whatsoever in *CubicWeb*. You are absolutely free to define whole view -families.) We juste arrange to wrap each blogentry output in a 'p' -html element. - -Next, we define the 'inblogcontext' view. This is NOT a primary view, -with its well-defined sections (title, metadata, attribtues, -relations/boxes). All a basic view has to define is cell_call. - -Since views are applied to result sets which can be tables of data, we -have to recover the entity from its (row,col)-coordinates (`line -20`). Then we can spit some HTML. - -But careful: all strings manipulated in *CubicWeb* are actually -unicode strings. While web browsers are usually tolerant to incoherent -encodings they are being served, we should not abuse it. Hence we have -to properly escape our data. The xml_escape() function has to be used -to safely fill (X)HTML elements from Python unicode strings. - - -**This is to be compared to interfaces and protocols in object-oriented -languages. Applying a given view called 'a_view' to all the entities -of a result set only requires to have for each entity of this result set, -an available view called 'a_view' which accepts the entity.** - -**Instead of merely using type based dispatch, we do predicate dispatch -which is quite more powerful.** - -Assuming we added entries to the blog titled `MyLife`, displaying it -now allows to read its description and all its entries. - -.. image:: ../../images/lax-book.10-blog-with-two-entries.en.png - :alt: a blog and all its entries - -**Before we move forward, remember that the selection/view principle is -at the core of *CubicWeb*. Everywhere in the engine, data is requested -using the RQL language, then HTML/XML/text/PNG is output by applying a -view to the result set returned by the query. That is where most of the -flexibility comes from.** - -[WRITE ME] - -* implementing interfaces, calendar for blog entries -* show that a calendar view can export data to ical - -We will implement the `cubicweb.interfaces.ICalendarable` interfaces on -entities.BlogEntry and apply the OneMonthCalendar and iCalendar views -to result sets like "Any E WHERE E is BlogEntry" - -* create view "blogentry table" with title, publish_date, category - -We will show that by default the view that displays -"Any E,D,C WHERE E publish_date D, E category C" is the table view. -Of course, the same can be obtained by calling -self.wview('table',rset) - -* in view blog, select blogentries and apply view "blogentry table" -* demo ajax by filtering blogentry table on category - -we did the same with 'primary', but with tables we can turn on filters -and show that ajax comes for free. -[FILLME] - - -XML views, binaries views... -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For views generating other formats than HTML (an image generated dynamically -for example), and which can not simply be included in the HTML page generated -by the main template (see above), you have to: - -* set the attribute `templatable` of the class to `False` -* set, through the attribute `content_type` of the class, the MIME type generated - by the view to `application/octet-stream` - -For views dedicated to binary content creation (like dynamically generated -images), we have to set the attribute `binary` of the class to `True` (which -implies that `templatable == False`, so that the attribute `w` of the view could be -replaced by a binary flow instead of unicode). diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/autoform.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/autoform.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,69 @@ +The automatic entity form (:mod:`cubicweb.web.views.autoform`) +--------------------------------------------------------------- + +Tags declaration +~~~~~~~~~~~~~~~~~~~~ + +It is possible to manage attributes/relations in the simple or multiple +editing form thanks of the methods bellow :: + + uicfg.autoform_section.tag_subject_of(, tag) + uicfg.autoform_section.tag_object_of(, tag) + uicfg.autoform_field.tag_attribute(, tag) + +Where ```` is a three elements tuple ``(Subject Entity Type, +relation_type, Object Entity Type)``. ```` is a two elements tuple +``(Entity Type, Attribut Name)``. Wildcard ``*`` could be used in place of +``Entity Type`` + +Possible tags are detailled below + +Simple Tags +~~~~~~~~~~~~~~~~~~~~ + +* `primary`, indicates that an attribute or a relation has to be + inserted **in the simple or multiple editing forms**. In the case of + a relation, the related entity editing form will be included in the + editing form and represented as a combobox. Each item of the + combobox is a link to an existing entity. + +* `secondary`, indicates that an attribute or a relation has to be + inserted **in the simple editing form only**. In the case of a + relation, the related entity editing form will be included in the + editing form and represented as a combobox. Each item of the combobox + is a link to an existing entity. + +* `inlineview`, includes the target entity's form in the editing form + of the current entity. It allows to create the target entity in the + same time as the current entity. + +* `generic`, indicates that a relation has to be inserted in the simple + editing form, in the generic box of relation creation. + +* `generated`, indicates that an attribute is dynamically computed + or other, and that it should not be displayed in the editing form. + +If necessary, it is possible to overwrite the method +`relation_category(rtype, x='subject')` to dynamically compute +a relation editing category. + + +Advanced Tags +~~~~~~~~~~~~~~~~~~~~ + +Tag can also reference a custom Field crafted with the help of +``cubicweb.web.formfields`` and ``cubicweb.web.formwidget``. In the example +bellow, the field ``path`` of ``ExecData`` entities will be done with a standard +file input dialogue :: + + from cubicweb.web import uicfg, formfields, formwidgets + + uicfg.autoform_field.tag_attribute(('Execdata', 'path'), + formfields.FileField(name='path', widget=formwidgets.FileInput())) + + + + + + + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/basetemplates.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/basetemplates.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,189 @@ +.. -*- coding: utf-8 -*- + +.. _templates: + +Templates +========= + +[WRITE ME] + +* talk about main templates, etc. + + + +Look at ``cubicweb/web/views/basetemplates.py`` and you will +find the base templates used to generate HTML for your application. + +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 +~~~~~~~~~~~~~~~~ + +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. + +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): + + def main_header(self, view): + """build the top menu with authentification info and the rql box""" + self.w(u'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + # logged user and help + #self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + + +.. 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. + +:: + + 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'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + + # logged user and help + #self.w(u'') + + self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + 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 : +:: + + form cubicweb.web.views.basetemplates import HTMLPageFooter + class MyHTMLPageFooter(HTMLPageFooter): + def call(self, **kwargs): + self.w(u'') + +Updating a view does not require any restart of the server. By reloading +the page you can see your new page footer. + + +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. + +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. + +We can also control certain aspects of the main template thanks to the following +forms parameters: + +* `__notemplate`, if present (whatever the value assigned), only the content view + is returned +* `__force_display`, if present and its value is not null, no navigation + whatever the number of entities to display +* `__method`, if the result set to render contains only one entity and this + parameter is set, it refers to a method to call on the entity by passing it + 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. diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/baseviews.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/baseviews.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,78 @@ +.. -*- coding: utf-8 -*- + +Base views (:mod:`cubicweb.web.views.baseviews`) +------------------------------------------------ + +*CubicWeb* provides a lot of standard views. You can find them in +``cubicweb/web/views/``. + +A certain number of views are used to build the web interface, which apply +to one or more entities. Their identifier is what distinguish them from +each others and the main ones are: + +HTML views +~~~~~~~~~~ + +Special views +````````````` + +*noresult* + This view is the default view used when no result has been found + (e.g. empty result set). + +*final* + Display the value of a cell without trasnformation (in case of a non final + entity, we see the eid). Applicable on any result set. + +*null* + This view is the default view used when nothing needs to be rendered. + It is always applicable and it does not return anything + +Entity views +```````````` +*incontext, outofcontext* + Those are used to display a link to an entity, depending on the + entity having to be displayed in or out of context + (of another entity). By default it respectively returns the + result of `textincontext` and `textoutofcontext` wrapped in a link + leading to the primary view of the entity. + +*oneline* + This view is used when we can't tell if the entity should be considered as + displayed in or out of context. By default it returns the result of `text` + in a link leading to the primary view of the entity. + +List +````` + +*list* + This view displays a list of entities by creating a HTML list (`
    `) + and call the view `listitem` for each entity of the result set. + +*listitem* + This view redirects by default to the `outofcontext` view. + +*sameetypelist* + This view displays a list of entities of the same type, in HTML section (`
    `) + and call the view `sameetypelistitem` for each entity of the result set. + +*sameetypelistitem* + This view redirects by default to the `listitem` view. + +*csv* + This view applies to entity groups, which are individually + displayed using the `incontext` view. It displays each entity as a + coma separated list. It is NOT related to the well-known text file + format. + +Text entity views +~~~~~~~~~~~~~~~~~ +*text* + This is the simplest text view for an entity. By default it returns the + result of the `.dc_title` method, which is cut to fit the + `navigation.short-line-size` property if necessary. + +*textincontext, textoutofcontext* + Similar to the `text` view, but called when an entity is considered out or + in context. By default it returns respectively the result of the + methods `.dc_title` and `.dc_long_title` of the entity. diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/boxes.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/boxes.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,34 @@ +Boxes (:mod:`cubicweb.web.views.boxes`) +--------------------------------------------------------------- + +*sidebox* + This view displays usually a side box of some related entities + in a primary view. + +The action box +~~~~~~~~~~~~~~~ + +The ``add_related`` is an automatic menu in the action box that allows to create +an entity automatically related to the initial entity (context in +which the box is displayed). By default, the links generated in this +box are computed from the schema properties of the displayed entity, +but it is possible to explicitly specify them thanks to the +`cubicweb.web.uicfg.rmode` *relation tag*: + +* `link`, indicates that a relation is in general created pointing + to an existing entity and that we should not to display a link + for this relation + +* `create`, indicates that a relation is in general created pointing + to new entities and that we should display a link to create a new + 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. + +Please note that if at least one action belongs to the `addrelated` category, +the automatic behavior is desactivated in favor of an explicit behavior +(e.g. display of `addrelated` category actions only). diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/breadcrumbs.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/breadcrumbs.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,3 @@ +Breadcrumbs (:mod:`cubicweb.web.views.ibreadcrumbs`) +---------------------------------------------------- +XXX feedme \ No newline at end of file diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/editforms.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/editforms.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,3 @@ +Standard forms (:mod:`cubicweb.web.views.editforms`) +---------------------------------------------------- +XXX feed me diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/embedding.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/embedding.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,9 @@ +.. -*- coding: utf-8 -*- + +Embedding external pages (:mod:`cubicweb.web.views.embedding`) +--------------------------------------------------------------- + +including external content + +XXX feeed me + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/facets.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/facets.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,3 @@ +Facets (:mod:`cubicweb.web.views.facets`) +----------------------------------------- +XXX feed me diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/idownloadable.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/idownloadable.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,3 @@ +The 'download' view (:mod:`cubicweb.web.views.idownloadable`) +--------------------------------------------------------------- + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,30 @@ +The views system +================ + +.. |cubicweb| replace:: *CubicWeb* + +This chapter aims to describe the concept of a `view` used all along +the development of a web application and how it has been implemented +in |cubicweb|. + + +.. toctree:: + :maxdepth: 3 + + views + basetemplates + primary + baseviews + startup + boxes + table + xmlrss + autoform + editforms + urlpublish + breadcrumbs + facets + wdoc + embedding + idownloadable + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/primary.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/primary.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,312 @@ +.. _primary_view: + +The Primary View +----------------- + +(:mod:`cubicweb.web.views.primary`) + +By default, *CubicWeb* provides a view that fits every available +entity type. This is the first view you might be interested in +modifying. It is also one of the richest and most complex. + +It is automatically selected on a one line result set containing an +entity. + +This view is supposed to render a maximum of informations about the +entity. + +.. _primary_view_layout: + +Layout +`````` + +The primary view has the following layout. + +.. image:: ../../../images/primaryview_template.png + +.. _primary_view_configuration: + +Primary view configuration +`````````````````````````` + +If you want to customize the primary view of an entity, overriding the primary +view class may not be necessary. For simple adjustments (attributes or relations +display locations and styles), a much simpler way is to use uicfg. + +Attributes/relations display location +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the primary view, there are 3 sections where attributes and +relations can be displayed (represented in pink in the image above): + +* attributes +* relations +* sideboxes + +**Attributes** can only be displayed in the attributes section (default + behavior). They can also be hidden. + +For instance, to hide the ``title`` attribute of the ``Blog`` entity: + +.. sourcecode:: python + + from cubicweb.web import uicfg + uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden') + +**Relations** can be either displayed in one of the three sections or hidden. + +For relations, there are two methods: + +* ``tag_object_of`` for modifying the primary view of the object +* ``tag_subject_of`` for modifying the primary view of the subject + +These two methods take two arguments: + +* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'`` +* the section name or ``hidden`` + +.. sourcecode:: python + + pv_section = uicfg.primaryview_section + # hide every relation `entry_of` in the `Blog` primary view + pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden') + + # display `entry_of` relations in the `relations` + # section in the `BlogEntry` primary view + pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations') + + +Display content +^^^^^^^^^^^^^^^ + +You can use ``primaryview_display_ctrl`` to customize the display of attributes +or relations. Values of ``primaryview_display_ctrl`` are dictionaries. + + +Common keys for attributes and relations are: + +* ``vid``: specifies the regid of the view for displaying the attribute or the relation. + + If ``vid`` is not specified, the default value depends on the section: + * ``attributes`` section: 'reledit' view + * ``relations`` section: 'autolimited' view + * ``sideboxes`` section: 'sidebox' view + +* ``order``: int used to control order within a section. When not specified, + automatically set according to order in which tags are added. + +.. sourcecode:: python + + # let us remind the schema of a blog entry + class BlogEntry(EntityType): + title = String(required=True, fulltextindexed=True, maxsize=256) + publish_date = Date(default='TODAY') + content = String(required=True, fulltextindexed=True) + entry_of = SubjectRelation('Blog', cardinality='?*') + + # now, we want to show attributes + # with an order different from that in the schema definition + view_ctrl = uicfg.primaryview_display_ctrl + for index, attr in enumerate('title', 'content', 'publish_date'): + view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index}) + +Keys for relations only: + +* ``label``: label for the relations section or side box + +* ``showlabel``: boolean telling whether the label is displayed + +* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed + +* ``filter``: callback taking the related result set as argument and returning it filtered + +.. sourcecode:: python + + pv_section = uicfg.primaryview_section + # in `CWUser` primary view, display `created_by` + # relations in relations section + pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations') + + # display this relation as a list, sets the label, + # limit the number of results and filters on comments + def filter_comment(rset): + return rset.filtered_rset(lambda x: x.e_schema == 'Comment') + pv_ctrl = uicfg.primaryview_display_ctrl + pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'), + {'vid': 'list', 'label': _('latest comment(s):'), + 'limit': True, + 'filter': filter_comment}) + +.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the + object of the relation is ignored for respectively ``tag_object_of`` or + ``tag_subject_of``. To avoid warnings during execution, they should be set to + ``'*'``. + +Rendering methods and attributes +```````````````````````````````` + +The basic layout of a primary view is as in the +:ref:`primary_view_layout` section. This layout is actually drawn by +the `render_entity` method. + +The methods you may want to modify while customizing a ``PrimaryView`` +are: + +*render_entity_title(self, entity)* + Renders the entity title using the ``def dc_title(self)`` method. + +*render_entity_metadata(self, entity)* + Renders the entity metadata by calling the ``metadata`` view on the + entity. This generic view is in cubicweb.views.baseviews. + +*render_entity_attributes(self, entity)* + Renders all the attribute of an entity with the exception of + attribute of type `Password` and `Bytes`. The skip_none class + attribute controls the display of None valued attributes. + +*render_entity_relations(self, entity)* + Renders all the relations of the entity in the main section of the page. + +*render_side_boxes(self, entity, boxes)* + Renders relations of the entity in a side box. + +The placement of relations in the relations section or in side boxes +can be controlled through the :ref:`primary_view_configuration` mechanism. + +*content_navigation_components(self, context)* + This method is applicable only for entity type implementing the interface + `IPrevNext`. This interface is for entities which can be linked to a previous + and/or next entity. This method will render the navigation links between + entities of this type, either at the top or at the bottom of the page + given the context (navcontent{top|bottom}). + +Also, please note that by setting the following attributes in your +subclass, you can already customize some of the rendering: + +*show_attr_label* + Renders the attribute label next to the attribute value if set to True. + Otherwise, does only display the attribute value. + +*show_rel_label* + Renders the relation label next to the relation value if set to True. + Otherwise, does only display the relation value. + +*skip_none* + Does not render an attribute value that is None if set to True. + +*main_related_section* + Renders the relations of the entity if set to True. + +A good practice is for you to identify the content of your entity type for which +the default rendering does not answer your need so that you can focus on the specific +method (from the list above) that needs to be modified. We do not advise you to +overwrite ``render_entity`` unless you want a completely different layout. + +Example of customization and creation +------------------------------------- + +We'll show you now an example of a ``primary`` view and how to customize it. + +We continue along the basic tutorial :ref:`tuto_blog`. + +If you want to change the way a ``BlogEntry`` is displayed, just override +the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``: + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.web.views.primary improt Primaryview + + class BlogEntryPrimaryView(PrimaryView): + __select__ = PrimaryView.__select__ & implements('BlogEntry') + + def render_entity_attributes(self, entity): + self.w(u'

    published on %s

    ' % + entity.publish_date.strftime('%Y-%m-%d')) + super(BlogEntryPrimaryView, self).render_entity_attributes(entity) + +The above source code defines a new primary view for +``BlogEntry``. The `id` class attribute is not repeated there since it +is inherited through the `primary.PrimaryView` class. + +The selector for this view chains the selector of the inherited class +with its own specific criterion. + +The view method ``self.w()`` is used to output data. Here `lines +08-09` output HTML for the publication date of the entry. + +.. image:: ../../../images/lax-book.09-new-view-blogentry.en.png + :alt: blog entries now look much nicer + +Let us now improve the primary view of a blog + +.. sourcecode:: python + + from logilab.mtconverter import xml_escape + from cubicweb.selectors import implements, one_line_rset + from cubicweb.web.views.primary import Primaryview + + class BlogPrimaryView(PrimaryView): + __regid__ = 'primary' + __select__ = PrimaryView.__select__ & implements('Blog') + rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s' + + def render_entity_relations(self, entity): + rset = self._cw.execute(self.rql, {'b' : entity.eid}) + for entry in rset.entities(): + self.w(u'

    %s

    ' % entry.view('inblogcontext')) + + class BlogEntryInBlogView(EntityView): + __regid__ = 'inblogcontext' + __select__ = implements('BlogEntry') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(row, col) + self.w(u'%s' % + entity.absolute_url(), + xml_escape(entity.content[:50]), + xml_escape(entity.description)) + +This happens in two places. First we override the +render_entity_relations method of a Blog's primary view. Here we want +to display our blog entries in a custom way. + +At `line 10`, a simple request is made to build a result set with all +the entities linked to the current ``Blog`` entity by the relationship +``entry_of``. The part of the framework handling the request knows +about the schema and infers that such entities have to be of the +``BlogEntry`` kind and retrieves them (in the prescribed publish_date +order). + +The request returns a selection of data called a result set. Result +set objects have an .entities() method returning a generator on +requested entities (going transparently through the `ORM` layer). + +At `line 13` the view 'inblogcontext' is applied to each blog entry to +output HTML. (Note that the 'inblogcontext' view is not defined +whatsoever in *CubicWeb*. You are absolutely free to define whole view +families.) We juste arrange to wrap each blogentry output in a 'p' +html element. + +Next, we define the 'inblogcontext' view. This is NOT a primary view, +with its well-defined sections (title, metadata, attribtues, +relations/boxes). All a basic view has to define is cell_call. + +Since views are applied to result sets which can be tables of data, we +have to recover the entity from its (row,col)-coordinates (`line +20`). Then we can spit some HTML. + +.. warning:: + + Be careful: all strings manipulated in *CubicWeb* are actually + unicode strings. While web browsers are usually tolerant to + incoherent encodings they are being served, we should not abuse + it. Hence we have to properly escape our data. The xml_escape() + function has to be used to safely fill (X)HTML elements from Python + unicode strings. + +Assuming we added entries to the blog titled `MyLife`, displaying it +now allows to read its description and all its entries. + +.. image:: ../../../images/lax-book.10-blog-with-two-entries.en.png + :alt: a blog and all its entries diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/startup.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/startup.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,13 @@ +Startup views (:mod:`cubicweb.web.views.startup`) +------------------------------------------------- +Usual selector: no_rset or yes. + +Views that don't apply to a result set + +*index* + This view defines the home page of your application. It does not require + a result set to apply to. + +*schema* + A view dedicated to the display of the schema of the instance + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/table.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/table.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,10 @@ +Table views (:mod:`cubicweb.web.views.table`) +---------------------------------------------- + +*table* + Creates a HTML table (``) and call the view `cell` for each cell of + the result set. Applicable on any result set. + +*cell* + By default redirects to the `final` view if this is a final entity or + `outofcontext` view otherwise diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/urlpublish.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/urlpublish.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,7 @@ +.. -*- coding: utf-8 -*- + +URL Rewriting (:mod:`cubicweb.web.views.urlpublish`) and (:mod:`cubicweb.web.views.urlrewrite`) +------------------------------------------------------------------------------------------------ + +XXX feed me +show how urls are mapped to selections and views and explain URLRewriting diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/views.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/views.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,127 @@ + +.. _Views: + +Principles +---------- + +We'll start with a description of the interface providing a basic +understanding of the available classes and methods, then detail the +view selection principle. + +A `View` is an object responsible for the rendering of data from the +model into an end-user consummable form. They typically churn out an +XHTML stream, but there are views concerned with email other non-html +outputs. + +Basic class for views +~~~~~~~~~~~~~~~~~~~~~ + +Class `View` (`cubicweb.view`) +``````````````````````````````` + +This class is an abstraction of a view class, used as a base class for every +renderable object such as views, templates, graphic components, etc. + +A `View` is instantiated to render a result set or part of a result +set. `View` subclasses may be parametrized using the following class +attributes: + +* `templatable` indicates if the view may be embedded in a main + template or if it has to be rendered standalone (i.e. pure XML views + must not be embedded in the main template of HTML pages) + +* if the view is not templatable, it should set the `content_type` + class attribute to the correct MIME type (text/xhtml being the + default) + +* the `category` attribute may be used in the interface to regroup + related view kinds together + +A view writes to its output stream thanks to its attribute `w` (the +append method of an `UStreamIO`, except for binary views). + +At instantiation time, the standard `_cw` and `cw_rset` attributes are +added and the `w` attribute will be set at rendering time. + +The basic interface for views is as follows (remember that the result +set has a tabular structure with rows and columns, hence cells): + +* `render(**context)`, render the view by calling `call` or + `cell_call` depending on the context + +* `call(**kwargs)`, call the view for a complete result set or null + (the default implementation calls `cell_call()` on each cell of the + result set) + +* `cell_call(row, col, **kwargs)`, call the view for a given cell of a + result set (`row` and `col` being integers used to access the cell) + +* `url()`, returns the URL enabling us to get the view with the current + result set + +* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of + identifier `__vid` on the given result set. It is possible to give a + fallback view identifier that will be used if the requested view is + not applicable to the result set. + +* `html_headers()`, returns a list of HTML headers to be set by the + main template + +* `page_title()`, returns the title to use in the HTML header `title` + +Other basic view classes +```````````````````````` +Here are some of the subclasses of `View` defined in `cubicweb.common.view` +that are more concrete as they relate to data rendering within the application: + +* `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) +* `StartupView`, start view that does not require a result set to apply to +* `AnyRsetView`, view applicable to any result set + +Examples of views class +``````````````````````` + +- Using `templatable`, `content_type` and HTTP cache configuration + +.. sourcecode:: python + + class RSSView(XMLView): + __regid__ = 'rss' + title = _('rss') + templatable = False + content_type = 'text/xml' + http_cache_manager = MaxAgeHTTPCacheManager + cache_max_age = 60*60*2 # stay in http cache for 2 hours by default + + +- Using a custom selector + +.. sourcecode:: python + + class SearchForAssociationView(EntityView): + """view called by the edition view when the user asks + to search for something to link to the edited eid + """ + __regid__ = 'search-associate' + title = _('search for association') + __select__ = one_line_rset() & match_search_state('linksearch') & implements('Any') + + + + +XML views, binaries views... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For views generating other formats than HTML (an image generated dynamically +for example), and which can not simply be included in the HTML page generated +by the main template (see above), you have to: + +* set the attribute `templatable` of the class to `False` +* set, through the attribute `content_type` of the class, the MIME + type generated by the view to `application/octet-stream` or any + relevant and more specialised mime type + +For views dedicated to binary content creation (like dynamically generated +images), we have to set the attribute `binary` of the class to `True` (which +implies that `templatable == False`, so that the attribute `w` of the view could be +replaced by a binary flow instead of unicode). diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/wdoc.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/wdoc.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,7 @@ +.. -*- coding: utf-8 -*- + +Online documentation system (:mod:`cubicweb.web.views.wdoc`) +------------------------------------------------------------- + +XXX describe the on-line documentation system + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/devweb/views/xmlrss.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/devweb/views/xmlrss.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,62 @@ +.. _XmlAndRss: + +XML and RSS views (:mod:`cubicweb.web.views.xmlrss`) +---------------------------------------------------- + +Overview ++++++++++ + +*rss* + Creates a RSS/XML view and call the view `rssitem` for each entity of + the result set. + +*rssitem* + Create a RSS/XML view for each entity based on the results of the dublin core + methods of the entity (`dc_*`) + + +RSS Channel Example +++++++++++++++++++++ + +Assuming you have several blog entries, click on the title of the +search box in the left column. A larger search box should appear. Enter:: + + Any X ORDERBY D WHERE X is BlogEntry, X creation_date D + +and you get a list of blog entries. + +Click on your login at the top right corner. Chose "user preferences", +then "boxes", then "possible views box" and check "visible = yes" +before validating your changes. + +Enter the same query in the search box and you will see the same list, +plus a box titled "possible views" in the left column. Click on +"entityview", then "RSS". + +You just applied the "RSS" view to the RQL selection you requested. + +That's it, you have a RSS channel for your blog. + +Try again with:: + + Any X ORDERBY D WHERE X is BlogEntry, X creation_date D, + X entry_of B, B title "MyLife" + +Another RSS channel, but a bit more focused. + +A last one for the road:: + + Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15 + +displayed with the RSS view, that's a channel for the last fifteen +comments posted. + +[WRITE ME] + +* show that the RSS view can be used to display an ordered selection + of blog entries, thus providing a RSS channel + +* show that a different selection (by category) means a different channel + + + diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/entityclasses/data-as-objects.rst --- a/doc/book/en/development/entityclasses/data-as-objects.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/entityclasses/data-as-objects.rst Thu Apr 15 12:48:40 2010 +0200 @@ -53,7 +53,7 @@ * `set_relations(**kwargs)`, add relations to the given object. To set a relation where this entity is the object of the relation, - use 'reverse_' as argument name. Values may be an + use `reverse_` as argument name. Values may be an entity, a list of entities, or None (meaning that all relations of the given type from or to this object should be deleted). diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/index.rst --- a/doc/book/en/development/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -1,8 +1,8 @@ .. _Part2: ---------------------- -Part II - Development ---------------------- +----------- +Development +----------- This part is about developing web applications with the *CubicWeb* framework. @@ -19,5 +19,4 @@ devrepo/index testing.rst migration.rst - webstdlib/index profiling.rst diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/testing.rst --- a/doc/book/en/development/testing.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/development/testing.rst Thu Apr 15 12:48:40 2010 +0200 @@ -3,9 +3,6 @@ Tests ===== -.. toctree:: - :maxdepth: 1 - Unit tests ---------- @@ -87,9 +84,12 @@ The test case itself checks that an Operation does it job of preventing cycles amongst Keyword entities. +You can see an example of security tests in the +:ref:`adv_tuto_security`. -XXX ref to advanced use case -XXX apycot plug +It is possible to have these tests run continuously using `apycot`_. + +.. _apycot: http://www.logilab.org/project/apycot Managing connections or users +++++++++++++++++++++++++++++ @@ -104,17 +104,31 @@ Before a self.login, one has to release the connection pool in use with a self.commit, self.rollback or self.close. +The `login` method returns a connection object that can be used as a +context manager: + +.. sourcecode:: python + + with self.login('user1') as user: + req = user.req + req.execute(...) + +On exit of the context manager, either a commit or rollback is issued, +which releases the connection. + When one is logged in as a normal user and wants to switch back to the -admin user, one has to use self.restore_connection(). +admin user without committing, one has to use +self.restore_connection(). -Usually it looks like this: +Usage with restore_connection: .. sourcecode:: python # execute using default admin connection self.execute(...) # I want to login with another user, ensure to free admin connection pool - # (could have used rollback but not close here, we should never close defaut admin connection) + # (could have used rollback but not close here + # we should never close defaut admin connection) self.commit() cnx = self.login('user') # execute using user connection @@ -130,8 +144,6 @@ Do not use the references kept to the entities created with a connection from another ! -XXX the new context manager ? - Email notifications tests ------------------------- @@ -182,21 +194,65 @@ test. The code here has to be uncommented to be usable, without further modification. -XXX more to come +The ``auto_populate`` method uses a smart algorithm to create +pseudo-random data in the database, thus enabling the views to be +invoked and tested. + +Depending on the schema, hooks and operations constraints, it is not +always possible for the automatic auto_populate to proceed. +It is possible of course to completely redefine auto_populate. A +lighter solution is to give hints (fill some class attributes) about +what entities and relations have to be skipped by the auto_populate +mechanism. These are: + +* `no_auto_populate`, may contain a list of entity types to skip +* `ignored_relations`, may contain a list of relation types to skip +* `application_rql`, may contain a list of rql expressions that + auto_populate cannot guess by itself; these must yield resultsets + against which views may be selected. + + +Test APIS +--------- Using Pytest ```````````` -.. automodule:: logilab.common.testlib +The `pytest` utility (shipping with `logilab-common`_, which is a +mandatory dependency of CubicWeb) extends the Python unittest +functionality and is the preferred way to run the CubicWeb test +suites. Bare unittests also work the usual way. + +.. _logilab-common: http://www.logilab.org/project/logilab-common + +To use it, you may: + +* just launch `pytest` in your cube to execute all tests (it will + discover them automatically) +* launch `pytest unittest_foo.py` to execute one test file +* launch `pytest unittest_foo.py bar` to execute all test methods and + all test cases whose name contain `bar` + +Additionally, the `-x` option tells pytest to exit at the first error +or failure. The `-i` option tells pytest to drop into pdb whenever an +exception occurs in a test. + +When the `-x` option has been used and the run stopped on a test, it +is possible, after having fixed the test, to relaunch pytest with the +`-R` option to tell it to start testing again from where it previously +failed. + +Using the `TestCase` base class +``````````````````````````````` + +The base class of CubicWebTC is logilab.common.testlib.TestCase, which +provides a lot of convenient assertion methods. + .. autoclass:: logilab.common.testlib.TestCase :members: -XXX pytestconf.py & options (e.g --source to use a different db -backend than sqlite) - CubicWebTC API `````````````` .. autoclass:: cubicweb.devtools.testlib.CubicWebTC :members: - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/autoform.rst --- a/doc/book/en/development/webstdlib/autoform.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -The automatic entity form (:mod:`cubicweb.web.views.autoform`) ---------------------------------------------------------------- - -Tags declaration -~~~~~~~~~~~~~~~~~~~~ - -It is possible to manage attributes/relations in the simple or multiple -editing form thanks of the methods bellow :: - - uicfg.autoform_section.tag_subject_of(, tag) - uicfg.autoform_section.tag_object_of(, tag) - uicfg.autoform_field.tag_attribute(, tag) - -Where ```` is a three elements tuple ``(Subject Entity Type, -relation_type, Object Entity Type)``. ```` is a two elements tuple -``(Entity Type, Attribut Name)``. Wildcard ``*`` could be used in place of -``Entity Type`` - -Possible tags are detailled below - -Simple Tags -~~~~~~~~~~~~~~~~~~~~ - -* `primary`, indicates that an attribute or a relation has to be - inserted **in the simple or multiple editing forms**. In the case of - a relation, the related entity editing form will be included in the - editing form and represented as a combobox. Each item of the - combobox is a link to an existing entity. - -* `secondary`, indicates that an attribute or a relation has to be - inserted **in the simple editing form only**. In the case of a - relation, the related entity editing form will be included in the - editing form and represented as a combobox. Each item of the combobox - is a link to an existing entity. - -* `inlineview`, includes the target entity's form in the editing form - of the current entity. It allows to create the target entity in the - same time as the current entity. - -* `generic`, indicates that a relation has to be inserted in the simple - editing form, in the generic box of relation creation. - -* `generated`, indicates that an attribute is dynamically computed - or other, and that it should not be displayed in the editing form. - -If necessary, it is possible to overwrite the method -`relation_category(rtype, x='subject')` to dynamically compute -a relation editing category. - - -Advanced Tags -~~~~~~~~~~~~~~~~~~~~ - -Tag can also reference a custom Field crafted with the help of -``cubicweb.web.formfields`` and ``cubicweb.web.formwidget``. In the example -bellow, the field ``path`` of ``ExecData`` entities will be done with a standard -file input dialogue :: - - from cubicweb.web import uicfg, formfields, formwidgets - - uicfg.autoform_field.tag_attribute(('Execdata', 'path'), - formfields.FileField(name='path', widget=formwidgets.FileInput())) - - - - - - - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/basetemplates.rst --- a/doc/book/en/development/webstdlib/basetemplates.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _templates: - -Templates -========= - -[WRITE ME] - -* talk about main templates, etc. - - - -Look at ``cubicweb/web/views/basetemplates.py`` and you will -find the base templates used to generate HTML for your application. - -A page is composed as indicated on the schema below : - -.. image:: ../../images/lax-book.06-main-template-layout.en.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 -~~~~~~~~~~~~~~~~ - -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. - -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): - - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - self.w(u'
    \n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - # logged user and help - #self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - - -.. 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. - -:: - - 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'\n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - - # logged user and help - #self.w(u'') - - self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - 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 : -:: - - form cubicweb.web.views.basetemplates import HTMLPageFooter - class MyHTMLPageFooter(HTMLPageFooter): - def call(self, **kwargs): - self.w(u'') - -Updating a view does not require any restart of the server. By reloading -the page you can see your new page footer. - - -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. - -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. - -We can also control certain aspects of the main template thanks to the following -forms parameters: - -* `__notemplate`, if present (whatever the value assigned), only the content view - is returned -* `__force_display`, if present and its value is not null, no navigation - whatever the number of entities to display -* `__method`, if the result set to render contains only one entity and this - parameter is set, it refers to a method to call on the entity by passing it - 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. diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/baseviews.rst --- a/doc/book/en/development/webstdlib/baseviews.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -.. -*- coding: utf-8 -*- - -Base views (:mod:`cubicweb.web.views.baseviews`) ------------------------------------------------- - -*CubicWeb* provides a lot of standard views. You can find them in -``cubicweb/web/views/``. - -A certain number of views are used to build the web interface, which apply -to one or more entities. Their identifier is what distinguish them from -each others and the main ones are: - -HTML views -~~~~~~~~~~ - -Special views -````````````` - -*noresult* - This view is the default view used when no result has been found - (e.g. empty result set). - -*final* - Display the value of a cell without trasnformation (in case of a non final - entity, we see the eid). Applicable on any result set. - -*null* - This view is the default view used when nothing needs to be rendered. - It is always applicable and it does not return anything - -Entity views -```````````` -*incontext, outofcontext* - Those are used to display a link to an entity, depending on the - entity having to be displayed in or out of context - (of another entity). By default it respectively returns the - result of `textincontext` and `textoutofcontext` wrapped in a link - leading to the primary view of the entity. - -*oneline* - This view is used when we can't tell if the entity should be considered as - displayed in or out of context. By default it returns the result of `text` - in a link leading to the primary view of the entity. - -List -````` - -*list* - This view displays a list of entities by creating a HTML list (`
      `) - and call the view `listitem` for each entity of the result set. - -*listitem* - This view redirects by default to the `outofcontext` view. - -*sameetypelist* - This view displays a list of entities of the same type, in HTML section (`
      `) - and call the view `sameetypelistitem` for each entity of the result set. - -*sameetypelistitem* - This view redirects by default to the `listitem` view. - -*csv* - This view applies to entity groups, which are individually - displayed using the `incontext` view. It displays each entity as a - coma separated list. It is NOT related to the well-known text file - format. - -Text entity views -~~~~~~~~~~~~~~~~~ -*text* - This is the simplest text view for an entity. By default it returns the - result of the `.dc_title` method, which is cut to fit the - `navigation.short-line-size` property if necessary. - -*textincontext, textoutofcontext* - Similar to the `text` view, but called when an entity is considered out or - in context. By default it returns respectively the result of the - methods `.dc_title` and `.dc_long_title` of the entity. diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/boxes.rst --- a/doc/book/en/development/webstdlib/boxes.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -Boxes (:mod:`cubicweb.web.views.boxes`) ---------------------------------------------------------------- - -*sidebox* - This view displays usually a side box of some related entities - in a primary view. - -The action box -~~~~~~~~~~~~~~~ - -The ``add_related`` is an automatic menu in the action box that allows to create -an entity automatically related to the initial entity (context in -which the box is displayed). By default, the links generated in this -box are computed from the schema properties of the displayed entity, -but it is possible to explicitly specify them thanks to the -`cubicweb.web.uicfg.rmode` *relation tag*: - -* `link`, indicates that a relation is in general created pointing - to an existing entity and that we should not to display a link - for this relation - -* `create`, indicates that a relation is in general created pointing - to new entities and that we should display a link to create a new - 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. - -Please note that if at least one action belongs to the `addrelated` category, -the automatic behavior is desactivated in favor of an explicit behavior -(e.g. display of `addrelated` category actions only). diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/breadcrumbs.rst --- a/doc/book/en/development/webstdlib/breadcrumbs.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -Breadcrumbs (:mod:`cubicweb.web.views.ibreadcrumbs`) ----------------------------------------------------- -XXX feedme \ No newline at end of file diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/editcontroller.rst --- a/doc/book/en/development/webstdlib/editcontroller.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,119 +0,0 @@ -.. -*- coding: utf-8 -*- - -The 'edit' controller (:mod:`cubicweb.web.views.editcontroller`) ----------------------------------------------------------------- - -Editing control -~~~~~~~~~~~~~~~~ - -Re-requisites: the parameters related to entities to edit are -specified as follows :: - - : - -where entity eid could be a letter in case of an entity to create. We -name those parameters as *qualified*. - -1. Retrieval of entities to edit by looking for the forms parameters - starting by `eid:` and also having a parameter `__type` associated - (also *qualified* by eid) - -2. For all the attributes and the relations of an entity to edit: - - 1. search for a parameter `edits-` or `edito-` - qualified in the case of a relation where the entity is object - 2. if found, the value returned is considered as the initial value - for this relaiton and we then look for the new value(s) in the parameter - (qualified) - 3. if the value returned is different from the initial value, an database update - request is done - -3. For each entity to edit: - - 1. if a qualified parameter `__linkto` is specified, its value has to be - a string (or a list of string) such as: :: - - :: - - where is either `subject` or `object` and each eid could be - separated from the others by a `_`. Target specifies if the *edited entity* - is subject or object of the relation and each relation specified will - be inserted. - - 2. if a qualified parameter `__clone_eid` is specified for an entity, the - relations of the specified entity passed as value of this parameter are - copied on the edited entity. - - 3. if a qualified parameter `__delete` is specified, its value must be - a string or a list of string such as follows: :: - - :: - - where each eid subject or object can be seperated from the other - by `_`. Each relation specified will be deleted. - - 4. if a qualified parameter `__insert` is specified, its value should - follow the same pattern as `__delete`, but each relation specified is - inserted. - -4. If the parameters `__insert` and/or `__delete` are found not qualified, - they are interpreted as explained above (independantly from the number - of entities edited). - -5. If no entity is edited but the form contains the parameters `__linkto` - and `eid`, this one is interpreted by using the value specified for `eid` - to designate the entity on which to add the relations. - - -.. note:: - - * If the parameter `__action_delete` is found, all the entities specified - as to be edited will be deleted. - - * If the parameter`__action_cancel` is found, no action is completed. - - * If the parameter `__action_apply` is found, the editing is applied - normally but the redirection is done on the form - (see :ref:`RedirectionControl`). - - * The parameter `__method` is also supported as for the main template - (XXX not very consistent, maybe __method should be dealed in the view - controller). - - * If no entity is found to be edited and if there is no parameter - `__action_delete`, `__action_cancel`, `__linkto`, `__delete` or - `__insert`, an error is raised. - - * Using the parameter `__message` in the form will allow to use its value - as a message to provide the user once the editing is completed. - - -.. _RedirectionControl: - -Redirection control -~~~~~~~~~~~~~~~~~~~ -Once editing is completed, there is still an issue left: where should we go -now? If nothing is specified, the controller will do his job but it does not -mean we will be happy with the result. We can control that by using the -following parameters: - -* `__redirectpath`: path of the URL (relative to the root URL of the site, - no form parameters - -* `__redirectparams`: forms parameters to add to the path - -* `__redirectrql`: redirection RQL request - -* `__redirectvid`: redirection view identifier - -* `__errorurl`: initial form URL, used for redirecting in case a validation - error is raised during editing. If this one is not specified, an error page - is displayed instead of going back to the form (which is, if necessary, - responsible for displaying the errors) - -* `__form_id`: initial view form identifier, used if `__action_apply` is - found - -In general we use either `__redirectpath` and `__redirectparams` or -`__redirectrql` and `__redirectvid`. - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/editforms.rst --- a/doc/book/en/development/webstdlib/editforms.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -Standard forms (:mod:`cubicweb.web.views.editforms`) ----------------------------------------------------- -XXX feed me diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/embedding.rst --- a/doc/book/en/development/webstdlib/embedding.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -.. -*- coding: utf-8 -*- - -Embedding external pages (:mod:`cubicweb.web.views.embedding`) ---------------------------------------------------------------- - -including external content - -XXX feeed me - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/facets.rst --- a/doc/book/en/development/webstdlib/facets.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -Facets (:mod:`cubicweb.web.views.facets`) ------------------------------------------ -XXX feed me diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/idownloadable.rst --- a/doc/book/en/development/webstdlib/idownloadable.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -The 'download' view (:mod:`cubicweb.web.views.idownloadable`) ---------------------------------------------------------------- - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/index.rst --- a/doc/book/en/development/webstdlib/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -Standard features for web interface development -=============================================== - -This chapter describes generic web features built as CubicWeb application objects. - -They are used for CubicWeb default automatic interface, but you're free to use -them or not for you're own application. - -.. toctree:: - :maxdepth: 1 - - basetemplates - primary - baseviews - startup - boxes - table - xmlrss - autoform - editforms - editcontroller - urlpublish - breadcrumbs - facets - wdoc - embedding - idownloadable \ No newline at end of file diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/primary.rst --- a/doc/book/en/development/webstdlib/primary.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,203 +0,0 @@ -.. _primary: - -The Primary View ------------------ - -(:mod:`cubicweb.web.views.primary`) - -By default, *CubicWeb* provides a view that fits every available -entity type. This is the first view you might be interested in -modifying. It is also one of the richest and most complex. - -It is automatically selected on a one line result set containing an -entity. - -This view is supposed to render a maximum of informations about the -entity. - -.. _primary_view_layout: - -Layout -`````` - -The primary view has the following layout. - -.. image:: ../../images/primaryview_template.png - -.. _primary_view_configuration: - -Primary view configuration -`````````````````````````` - -If you want to customize the primary view of an entity, overriding the primary -view class may not be necessary. For simple adjustments (attributes or relations -display locations and styles), a much simpler way is to use uicfg. - -Attributes/relations display location -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the primary view, there are 3 sections where attributes and -relations can be displayed (represented in pink in the image above): - -* attributes -* relations -* sideboxes - -**Attributes** can only be displayed in the attributes section (default - behavior). They can also be hidden. - -For instance, to hide the ``title`` attribute of the ``Blog`` entity: - -.. sourcecode:: python - - from cubicweb.web import uicfg - uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden') - -**Relations** can be either displayed in one of the three sections or hidden. - -For relations, there are two methods: - -* ``tag_object_of`` for modifying the primary view of the object -* ``tag_subject_of`` for modifying the primary view of the subject - -These two methods take two arguments: - -* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'`` -* the section name or ``hidden`` - -.. sourcecode:: python - - pv_section = uicfg.primaryview_section - # hide every relation `entry_of` in the `Blog` primary view - pv_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden') - - # display `entry_of` relations in the `relations` - # section in the `BlogEntry` primary view - pv_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), 'relations') - - -Display content -^^^^^^^^^^^^^^^ - -You can use ``primaryview_display_ctrl`` to customize the display of attributes -or relations. Values of ``primaryview_display_ctrl`` are dictionaries. - - -Common keys for attributes and relations are: - -* ``vid``: specifies the regid of the view for displaying the attribute or the relation. - - If ``vid`` is not specified, the default value depends on the section: - * ``attributes`` section: 'reledit' view - * ``relations`` section: 'autolimited' view - * ``sideboxes`` section: 'sidebox' view - -* ``order``: int used to control order within a section. When not specified, - automatically set according to order in which tags are added. - -.. sourcecode:: python - - # let us remind the schema of a blog entry - class BlogEntry(EntityType): - title = String(required=True, fulltextindexed=True, maxsize=256) - publish_date = Date(default='TODAY') - content = String(required=True, fulltextindexed=True) - entry_of = SubjectRelation('Blog', cardinality='?*') - - # now, we want to show attributes - # with an order different from that in the schema definition - view_ctrl = uicfg.primaryview_display_ctrl - for index, attr in enumerate('title', 'content', 'publish_date'): - view_ctrl.tag_attribute(('BlogEntry', attr), {'order': index}) - -Keys for relations only: - -* ``label``: label for the relations section or side box - -* ``showlabel``: boolean telling whether the label is displayed - -* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed - -* ``filter``: callback taking the related result set as argument and returning it filtered - -.. sourcecode:: python - - pv_section = uicfg.primaryview_section - # in `CWUser` primary view, display `created_by` - # relations in relations section - pv_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations') - - # display this relation as a list, sets the label, - # limit the number of results and filters on comments - def filter_comment(rset): - return rset.filtered_rset(lambda x: x.e_schema == 'Comment') - pv_ctrl = uicfg.primaryview_display_ctrl - pv_ctrl.tag_object_of(('*', 'created_by', 'CWUser'), - {'vid': 'list', 'label': _('latest comment(s):'), - 'limit': True, - 'filter': filter_comment}) - -.. warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the - object of the relation is ignored for respectively ``tag_object_of`` or - ``tag_subject_of``. To avoid warnings during execution, they should be set to - ``'*'``. - -Rendering methods and attributes -```````````````````````````````` - -The basic layout of a primary view is as in the -:ref:`primary_view_layout` section. This layout is actually drawn by -the `render_entity` method. - -The methods you may want to modify while customizing a ``PrimaryView`` -are: - -*render_entity_title(self, entity)* - Renders the entity title using the ``def dc_title(self)`` method. - -*render_entity_metadata(self, entity)* - Renders the entity metadata by calling the ``metadata`` view on the - entity. This generic view is in cubicweb.views.baseviews. - -*render_entity_attributes(self, entity)* - Renders all the attribute of an entity with the exception of - attribute of type `Password` and `Bytes`. The skip_none class - attribute controls the display of None valued attributes. - -*render_entity_relations(self, entity)* - Renders all the relations of the entity in the main section of the page. - -*render_side_boxes(self, entity, boxes)* - Renders relations of the entity in a side box. - -The placement of relations in the relations section or in side boxes -can be controlled through the :ref:`primary_view_configuration` mechanism. - -*content_navigation_components(self, context)* - This method is applicable only for entity type implementing the interface - `IPrevNext`. This interface is for entities which can be linked to a previous - and/or next entity. This method will render the navigation links between - entities of this type, either at the top or at the bottom of the page - given the context (navcontent{top|bottom}). - -Also, please note that by setting the following attributes in your -subclass, you can already customize some of the rendering: - -*show_attr_label* - Renders the attribute label next to the attribute value if set to True. - Otherwise, does only display the attribute value. - -*show_rel_label* - Renders the relation label next to the relation value if set to True. - Otherwise, does only display the relation value. - -*skip_none* - Does not render an attribute value that is None if set to True. - -*main_related_section* - Renders the relations of the entity if set to True. - -A good practice is for you to identify the content of your entity type for which -the default rendering does not answer your need so that you can focus on the specific -method (from the list above) that needs to be modified. We do not advise you to -overwrite ``render_entity`` unless you want a completely different layout. diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/startup.rst --- a/doc/book/en/development/webstdlib/startup.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -Startup views (:mod:`cubicweb.web.views.startup`) -------------------------------------------------- -Usual selector: no_rset or yes. - -Views that don't apply to a result set - -*index* - This view defines the home page of your application. It does not require - a result set to apply to. - -*schema* - A view dedicated to the display of the schema of the instance - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/table.rst --- a/doc/book/en/development/webstdlib/table.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -Table views (:mod:`cubicweb.web.views.table`) ----------------------------------------------- - -*table* - Creates a HTML table (``) and call the view `cell` for each cell of - the result set. Applicable on any result set. - -*cell* - By default redirects to the `final` view if this is a final entity or - `outofcontext` view otherwise diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/urlpublish.rst --- a/doc/book/en/development/webstdlib/urlpublish.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -.. -*- coding: utf-8 -*- - -URL Rewriting (:mod:`cubicweb.web.views.urlpublish`) and (:mod:`cubicweb.web.views.urlrewrite`) ------------------------------------------------------------------------------------------------- - -XXX feed me -show how urls are mapped to selections and views and explain URLRewriting diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/wdoc.rst --- a/doc/book/en/development/webstdlib/wdoc.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -.. -*- coding: utf-8 -*- - -Online documentation system (:mod:`cubicweb.web.views.wdoc`) -------------------------------------------------------------- - -XXX describe the on-line documentation system - diff -r f7d2df59231a -r 16461f675734 doc/book/en/development/webstdlib/xmlrss.rst --- a/doc/book/en/development/webstdlib/xmlrss.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -.. _XmlAndRss: - -XML and RSS views (:mod:`cubicweb.web.views.xmlrss`) ----------------------------------------------------- - -Overview -+++++++++ - -*rss* - Creates a RSS/XML view and call the view `rssitem` for each entity of - the result set. - -*rssitem* - Create a RSS/XML view for each entity based on the results of the dublin core - methods of the entity (`dc_*`) - - -RSS Channel Example -++++++++++++++++++++ - -Assuming you have several blog entries, click on the title of the -search box in the left column. A larger search box should appear. Enter:: - - Any X ORDERBY D WHERE X is BlogEntry, X creation_date D - -and you get a list of blog entries. - -Click on your login at the top right corner. Chose "user preferences", -then "boxes", then "possible views box" and check "visible = yes" -before validating your changes. - -Enter the same query in the search box and you will see the same list, -plus a box titled "possible views" in the left column. Click on -"entityview", then "RSS". - -You just applied the "RSS" view to the RQL selection you requested. - -That's it, you have a RSS channel for your blog. - -Try again with:: - - Any X ORDERBY D WHERE X is BlogEntry, X creation_date D, - X entry_of B, B title "MyLife" - -Another RSS channel, but a bit more focused. - -A last one for the road:: - - Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15 - -displayed with the RSS view, that's a channel for the last fifteen -comments posted. - -[WRITE ME] - -* show that the RSS view can be used to display an ordered selection - of blog entries, thus providing a RSS channel - -* show that a different selection (by category) means a different channel - - - diff -r f7d2df59231a -r 16461f675734 doc/book/en/images/main_template.png Binary file doc/book/en/images/main_template.png has changed diff -r f7d2df59231a -r 16461f675734 doc/book/en/images/main_template.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/images/main_template.svg Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,207 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + header + + + + + contentheader + + + + footer + + + + + contentfooter + + left column + contentcol + contentmain + + diff -r f7d2df59231a -r 16461f675734 doc/book/en/images/primaryview_template.png Binary file doc/book/en/images/primaryview_template.png has changed diff -r f7d2df59231a -r 16461f675734 doc/book/en/images/primaryview_template.svg --- a/doc/book/en/images/primaryview_template.svg Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/images/primaryview_template.svg Thu Apr 15 12:48:40 2010 +0200 @@ -36,16 +36,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.49497475" - inkscape:cx="432.61573" - inkscape:cy="370.11733" + inkscape:zoom="0.9357135" + inkscape:cx="518.32104" + inkscape:cy="337.0428" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="824" - inkscape:window-height="1094" + inkscape:window-width="1307" + inkscape:window-height="1168" inkscape:window-x="0" - inkscape:window-y="45" /> + inkscape:window-y="0" /> @@ -62,193 +62,173 @@ inkscape:groupmode="layer" id="layer1" transform="translate(162.2968,90.697922)"> + + + contentmain + - + width="772.32111" + height="43.888428" + x="-131.1837" + y="86.559296" /> navcontenttop + width="770.26868" + height="203.16078" + x="-125.88269" + y="172.90417" /> view.render_entity_attributes() + x="348.26724" + y="205.34305" + id="tspan3171">render_entity_attributes() + width="769.93549" + height="237.84663" + x="-125.03326" + y="391.32156" /> view.render_entity_relations() + x="360.99954" + y="428.38055" + id="tspan3177">render_entity_relations() + width="178.93939" + height="612.36584" + x="667.10443" + y="84.64225" /> view.render_side_boxes() + id="tspan2408">render_side_boxes() + width="771.97766" + height="55.647793" + x="-127.80586" + y="642.0293" /> navcontentbottom + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3881" + width="986.90503" + height="45.800392" + x="-128.34428" + y="-31.574066" /> header + x="355.60541" + y="-2.7424495" + id="tspan3885">render_entity_toolbox(), render_entity_title() - + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1.68198514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3890" + width="986.90503" + height="45.800392" + x="-128.87863" + y="19.723684" /> contentheader - + x="565.71027" + y="50.135612" + id="tspan3894">render_entity_summary() footer - + id="tspan3903" + x="87.154541" + y="114.2578">content_navigation_components('navcontenttop') contentfooter - leftcolumn - view.render() + id="tspan2412" + x="88.46772" + y="675.71582">content_navigation_components('navcontenttop') diff -r f7d2df59231a -r 16461f675734 doc/book/en/index.rst --- a/doc/book/en/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -44,18 +44,25 @@ ================= .. toctree:: + :maxdepth: 2 + + intro/index + tutorials/index + +.. toctree:: :maxdepth: 3 - intro/index development/index + +.. toctree:: + :maxdepth: 2 + admin/index annexes/index See also: -* the complete :ref:`TOC`, * the :ref:`genindex`, * the :ref:`modindex`, -* and the :ref:`search`. .. |cubicweb| replace:: *CubicWeb* diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/book-map.rst --- a/doc/book/en/intro/book-map.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -.. -*- coding: utf-8 -*- - -Book map -======== - -[XXX WRITE ME] - -* explain how to use this book and what chapters to read in what order depending on the - objectives of the reader - diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/concepts.rst --- a/doc/book/en/intro/concepts.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/intro/concepts.rst Thu Apr 15 12:48:40 2010 +0200 @@ -59,7 +59,7 @@ repository. For applications that support high traffic, several web (front-end) and data (back-end) instances can be set-up to share the load. -.. image:: ../../images/archi_globale.en.png +.. image:: ../images/archi_globale.en.png The command :command:`cubicweb-ctl list` also displays the list of instances installed on your system. diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/index.rst --- a/doc/book/en/intro/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ b/doc/book/en/intro/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -2,9 +2,9 @@ .. _Part1: ------------------------------------ -Part I - Introduction to *CubicWeb* ------------------------------------ +-------------------------- +Introduction to *CubicWeb* +-------------------------- This first part of the book offers different reading path to discover the *CubicWeb* framework, provides a tutorial to get a quick @@ -15,7 +15,5 @@ :maxdepth: 2 :numbered: - book-map history concepts.rst - tutorial/index diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/blog-in-five-minutes.rst --- a/doc/book/en/intro/tutorial/blog-in-five-minutes.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _BlogFiveMinutes: - -Get a blog running in five minutes! ------------------------------------ - -For Debian or Ubuntu users, first install the following packages (:ref:`DebianInstallation`):: - - cubicweb, cubicweb-dev, cubicweb-blog - -For Windows or Mac OS X users, you must install cubicweb from source (see :ref:`SourceInstallation` and :ref:`WindowsInstallation`). - -Then create and initialize your instance:: - - cubicweb-ctl create blog myblog - -And start it:: - - cubicweb-ctl start -D myblog - -The -D option is the debugging mode of cubicweb, removing it will lauch the instance in the background. - -Permission -~~~~~~~~~~ - -This command assumes that you have root access to the /etc/ path. In order to initialize your instance as a `user` (from scratch), please check your current PYTHONPATH then create the ~/etc/cubicweb.d directory. - -Instance parameters -~~~~~~~~~~~~~~~~~~~ - -If the database installation failed, you'd like to change some instance parameters, for example, the database host or the user name. These informations can be edited in the `source` file located in the /etc/cubicweb.d/myblog directory. - -Then relaunch the database creation: - - cubicweb-ctl db-create myblog - -Other paramaters, like web server or emails parameters, can be modified in the `all-in-one.conf` file. - -This is it. Your blog is running. Visit http://localhost:8080 and enjoy it! This blog is fully functionnal. The next section section will present the way to develop new cubes and customizing the look of your instance. - - diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/components.rst --- a/doc/book/en/intro/tutorial/components.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _cubes: - -Cubes ------ - -Standard library -~~~~~~~~~~~~~~~~ - -A library of standard cubes are available from `CubicWeb Forge`_ -Cubes provide entities and views. - -The available application entities in standard cubes are: - -* addressbook: PhoneNumber and PostalAddress - -* basket: Basket (like a shopping cart) - -* blog: Blog (a *very* basic blog) - -* classfolder: Folder (to organize things but grouping them in folders) - -* classtags: Tag (to tag anything) - -* comment: Comment (to attach comment threads to entities) - -* file: File (to allow users to upload and store binary or text files) - -* link: Link (to collect links to web resources) - -* mailinglist: MailingList (to reference a mailing-list and the URLs - for its archives and its admin interface) - -* person: Person (easily mixed with addressbook) - -* task: Task (something to be done between start and stop date) - -* zone: Zone (to define places within larger places, for example a - city in a state in a country) - -.. _`CubicWeb Forge`: http://www.cubicweb.org/project/ - -Adding comments to BlogDemo -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To import a cube in your instance just change the line in the -``__pkginfo__.py`` file and verify that the cube you are planning -to use is listed by the command ``cubicweb-ctl list``. -For example:: - - __use__ = ('comment',) - -will make the ``Comment`` entity available in your ``BlogDemo`` -cube. - -Change the schema to add a relationship between ``BlogEntry`` and -``Comment`` and you are done. Since the comment cube defines the -``comments`` relationship, adding the line:: - - comments = ObjectRelation('Comment', cardinality='1*', composite='object') - -to the definition of a ``BlogEntry`` will be enough. - -Synchronize the data model -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once you modified your data model, you need to synchronize the -database with your model. For this purpose, *CubicWeb* provides -a very useful command ``cubicweb-ctl shell blogdemo`` which -launches an interactive shell where you can enter migration -commands (see :ref:`cubicweb-ctl` for more details)). -As you added the cube named `comment`, you need to run: - -:: - - add_cube('comment') - -You can now start your instance and comment your blog entries. diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/conclusion.rst --- a/doc/book/en/intro/tutorial/conclusion.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -.. -*- coding: utf-8 -*- - -What's next? ------------- - -We demonstrated how from a straight out of the box *CubicWeb* installation, you -can build your web application based on a data model. It's all already there: -views, templates, permissions, etc. The step forward is now for you to customize -according to your needs. - -Many features are available to extend your application, for example: RSS channel -integration (:ref:`XmlAndRss`), hooks (:ref:`hooks`), support of sources such as -Google App Engine (:ref:`GoogleAppEngineSource`) and lots of others to discover -through our book. - diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/create-cube.rst --- a/doc/book/en/intro/tutorial/create-cube.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,430 +0,0 @@ -.. -*- coding: utf-8 -*- - - -.. _Steps: - -Steps for creating your cube ----------------------------- - -The following steps will help you to create and customize a new cube. - -1. :ref:`CreateYourCube` - -Create the directory to hold the code of your cube. The most important files that will be useful to customize your newly created cube are: - * schema.py: contains the data model - * views.py: contains your custom views - * entities.py: contains XXX - -The detailed structure of the code directory is described in :ref:`cubelayout`. - -2. :ref:`DefineDataModel` - -Define the data model of your application. - -3. :ref:`ExploreYourInstance` - -Create, run, and explore an instance of your cube. - -4. :ref:`DefineViews` - -Customize the views of your data: how and which part of your data are showed. - -Note: views don't concern the look'n'feel or design of the site. For that, you should use CSS instead, and default CSS or your new cube are located in 'blog/data/'. - - -5. :ref:`DefineEntities` - -Define your own entities to add useful functions when you manipulate your data, especially when you write view. - - -.. _CreateYourCube: - -Create your cube ----------------- - -The packages ``cubicweb`` and ``cubicweb-dev`` install a command line -tool for *CubicWeb* called ``cubicweb-ctl``. This tool provides a wide -range of commands described in details in :ref:`cubicweb-ctl`. - -Once your *CubicWeb* development environment is set up, you can create -a new cube:: - - cubicweb-ctl newcube blog - -This will create in the cubes directory (``/path/to/forest/cubes`` for Mercurial -installation, ``/usr/share/cubicweb/cubes`` for debian packages installation) -a directory named ``blog`` reflecting the structure described in :ref:`Concepts`. - - -For packages installation, you can still create new cubes in your home directory using the following configuration. Let's say you want to develop your new cubes in `~src/cubes`, then set the following environment variables: -:: - - CW_CUBES_PATH=~/src/cubes - CW_MODE=user - -and then create your new cube using: -:: - - cubicweb-ctl newcube --directory=~/src/cubes blog - - - - - - -.. _DefineDataModel: - -Define your data model ----------------------- - -The data model or schema is the core of your *CubicWeb* application. -It defines the type of content your application will handle. - -The data model of your cube ``blog`` is defined in the file ``schema.py``: - -.. sourcecode:: python - - from yams.buildobjs import EntityType, String, SubjectRelation, Date - - class Blog(EntityType): - title = String(maxsize=50, required=True) - description = String() - - class BlogEntry(EntityType): - title = String(required=True, fulltextindexed=True, maxsize=256) - publish_date = Date(default='TODAY') - content = String(required=True, fulltextindexed=True) - entry_of = SubjectRelation('Blog', cardinality='?*') - -The first step is the import of the EntityType (generic class for entity and -attributes that will be used in both Blog and BlogEntry entities. - -A Blog has a title and a description. The title is a string that is -required and must be less than 50 characters. The -description is a string that is not constrained. - -A BlogEntry has a title, a publish_date and a content. The title is a -string that is required and must be less than 100 characters. The -publish_date is a Date with a default value of TODAY, meaning that -when a BlogEntry is created, its publish_date will be the current day -unless it is modified. The content is a string that will be indexed in -the database full-text index and has no constraint. - -A BlogEntry also has a relationship ``entry_of`` that links it to a -Blog. The cardinality ``?*`` means that a BlogEntry can be part of -zero or one Blog (``?`` means `zero or one`) and that a Blog can -have any number of BlogEntry (``*`` means `any number including -zero`). For completeness, remember that ``+`` means `one or more`. - - -.. _ExploreYourInstance: - -Create and explore your instance --------------------------------- -.. _CreateYourInstance: - -Create your instance -~~~~~~~~~~~~~~~~~~~~ - -To use this cube as an instance and create a new instance named ``blogdemo``, do:: - - cubicweb-ctl create blog blogdemo - -This command will create the corresponding database and initialize it. - - -.. _WelcomeToYourWebInstance: - -Welcome to your web instance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Start your instance in debug mode with the following command: :: - - cubicweb-ctl start -D blogdemo - - -You can now access your web instance to create blogs and post messages -by visiting the URL http://localhost:8080/. - -A login form will appear. By default, the instance will not allow anonymous -users to enter the instance. To login, you need then use the admin account -you created at the time you initialized the database with ``cubicweb-ctl -create``. - -.. image:: ../../images/login-form.png - - -Once authenticated, you can start playing with your instance -and create entities. - -.. image:: ../../images/blog-demo-first-page.png - -Please notice that so far, the *CubicWeb* framework managed all aspects of -the web application based on the schema provided at the beginning. - -.. _AddEntities: - -Add entities -~~~~~~~~~~~~ - -We will now add entities in our web application. - -Add a Blog -********** - -Let us create a few of these entities. Click on the `[+]` at the left of the -link Blog on the home page. Call this new Blog ``Tech-blog`` and type in -``everything about technology`` as the description, then validate the form by -clicking on ``Validate``. - -.. image:: ../../images/cbw-create-blog.en.png - :alt: from to create blog - -Click on the logo at top left to get back to the home page, then -follow the Blog link that will list for you all the existing Blog. -You should be seeing a list with a single item ``Tech-blog`` you -just created. - -.. image:: ../../images/cbw-list-one-blog.en.png - :alt: displaying a list of a single blog - -Clicking on this item will get you to its detailed description except -that in this case, there is not much to display besides the name and -the phrase ``everything about technology``. - -Now get back to the home page by clicking on the top-left logo, then -create a new Blog called ``MyLife`` and get back to the home page -again to follow the Blog link for the second time. The list now -has two items. - -.. image:: ../../images/cbw-list-two-blog.en.png - :alt: displaying a list of two blogs - -Add a BlogEntry -*************** - -Get back to the home page and click on [+] at the left of the link -BlogEntry. Call this new entry ``Hello World`` and type in some text -before clicking on ``Validate``. You added a new blog entry without -saying to what blog it belongs. There is a box on the left entitled -``actions``, click on the menu item ``modify``. You are back to the form -to edit the blog entry you just created, except that the form now has -another section with a combobox titled ``add relation``. Chose -``entry_of`` in this menu and a second combobox appears where you pick -``MyLife``. - -You could also have, at the time you started to fill the form for a -new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the -combobox titled ``add relation`` would have showed up. - - -.. image:: ../../images/cbw-add-relation-entryof.en.png - :alt: editing a blog entry to add a relation to a blog - -Validate the changes by clicking ``Validate``. The entity BlogEntry -that is displayed now includes a link to the entity Blog named -``MyLife``. - -.. image:: ../../images/cbw-detail-one-blogentry.en.png - :alt: displaying the detailed view of a blogentry - -Note that all of this was handled by the framework and that the only input -that was provided so far is the schema. To get a graphical view of the schema, -point your browser to the URL http://localhost:8080/schema - -.. image:: ../../images/cbw-schema.en.png - :alt: graphical view of the schema (aka data-model) - - -.. _DefineViews: - -Define your entity views ------------------------- - -Each entity defined in a model is associated with default views -allowing different rendering of the data. You can redefine each of -them according to your needs and preferences. So let's see how the -views are defined. - - -The view selection principle -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A view is defined by a Python class which includes: - - - an identifier (all objects in *CubicWeb* are recorded in a - registry and this identifier will be used as a key) - - - a filter to select the result sets it can be applied to - -A view has a set of methods complying with the `View` class interface -(`cubicweb.common.view`). - -*CubicWeb* provides a lot of standard views for the type `EntityView`; -for a complete list, read the code in directory ``cubicweb/web/views/``. - -A view is applied on a `result set` which contains a set of entities -we are trying to display. *CubicWeb* uses a selector mechanism which -computes for each available view a score: the view with the highest -score is then used to display the given `result set`. The standard -library of selectors is in ``cubicweb.selector``. - -It is possible to define multiple views for the same identifier -and to associate selectors and filters to allow the application -to find the most appropriate way to render the data. - -For example, the view named ``primary`` is the one used to display a -single entity. We will now show you how to create a primary view for -BlogEntry. - - -Primary view customization -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you wish to modify the way a `BlogEntry` is rendered, you will have -to subclass the `primary` view, for instance in the module ``views`` -of the cube ``cubes/blog/views.py``. - -The standard primary view is the most sophisticated view of all. It -has more than a call() method. It is a template. Actually the entry -point calls the following sequence of (redefinable) methods: - - * render_entity_title - - * render_entity_metadata - - * render_entity_attributes - - * render_entity_relations - - * render_side_boxes - -Excepted side boxes, we can see all of them already in action in the -blog entry view. This is all described in more details in -:ref:`primary`. - -We can for example add in front of the publication date a prefix -specifying that the date we see is the publication date. - -To do so, please apply the following changes: - -.. sourcecode:: python - - from cubicweb.selectors import implements - from cubicweb.web.views import primary - - class BlogEntryPrimaryView(primary.PrimaryView): - __select__ = implements('BlogEntry') - - def render_entity_attributes(self, entity): - self.w(u'

      published on %s

      ' % - entity.publish_date.strftime('%Y-%m-%d')) - super(BlogEntryPrimaryView, self).render_entity_attributes(entity) - -.. note:: - When a view is modified, it is not required to restart the instance - server. Save the Python file and reload the page in your web browser - to view the changes. - -You can now see that the publication date has a prefix. - -.. image:: ../../images/cbw-update-primary-view.en.png - :alt: modified primary view - - -The above source code defines a new primary view for ``BlogEntry``. - -Since views are applied to result sets and result sets can be tables of -data, we have to recover the entity from its (row,col)-coordinates. -The view has a ``self.w()`` method that is used to output data, in our -example HTML output. - -.. note:: - You can find more details about views and selectors in :ref:`Views`. - - -.. _DefineEntities: - -Write entities to add logic in your data ----------------------------------------- - -By default, CubicWeb provides a default entity for each data type defined in the schema. -A default entity mainly contains the attributes defined in the data model. - -You can redefine each entity to provide additional functions to help you write your views. - -.. sourcecode:: python - - from cubicweb.entities import AnyEntity - - class BlogEntry(AnyEntity): - """customized class for BlogEntry entities""" - __regid__ = 'BlogEntry' - __implements__ = AnyEntity.__implements__ - - def display_cw_logo(self): - if 'CW' in self.title: - return True - else: - return False - -Customizing an entity requires that your entity: - - inherits from ``cubicweb.entities`` or any subclass - - defines a ``__regid__`` linked to the corresponding data type of your schema - - implements the base class by explicitly using ``__implements__``. - -We implemented here a function ``display_cw_logo`` which tests if the blog entry title contains 'CW'. -This function can then be used when you customize your views. For instance, you can modify your previous ``views.py`` as follows: - -.. sourcecode:: python - - class BlogEntryPrimaryView(primary.PrimaryView): - __select__ = implements('BlogEntry') - - ... - - def render_entity_title(self, entity): - if entity.display_cw_logo(): - self.w(u'') - super(BlogEntryPrimaryView, self).render_entity_title(entity) - -Then each blog entry whose title contains 'CW' is shown with the CubicWeb logo in front of it. - -.. _UpdatingSchemaAndSynchronisingInstance: - -Updating the schema and synchronising the instance --------------------------------------------------- - -While developping your cube, you may want to update your data model. Let's say you -want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration. - -The required steps are: -1. modify the file ``schema.py``. The ``Blog`` class looks now like this: - -.. sourcecode:: python - - class Blog(EntityType): - title = String(maxsize=50, required=True) - description = String() - category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal') - -2. stop your ``blogdemo`` instance - -3. start the cubicweb shell for your instance by running the following command: - -.. sourcecode:: bash - - cubicweb-ctl shell blogdemo - -4. in the shell, execute: - -.. sourcecode:: python - - add_attribute('Blog', 'category') - -5. you can restart your instance, modify a blog entity and check that the new attribute -``category`` has been added. - -Of course, you may also want to add relations, entity types, ... See :ref:`migration` -for a list of all available migration commands. - diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/index.rst --- a/doc/book/en/intro/tutorial/index.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -.. -*- coding: utf-8 -*- - -.. _Tutorial: - -Tutorial -======== - -*CubicWeb* is a semantic web application framework that favors reuse and -object-oriented design. - -A `cube` is a component that includes a model defining the data types and a set of -views to display the data. A cube can be built by assembling other cubes. - -An `instance` is a specific installation of a cube and includes configuration -files. - - -This tutorial will show how to create a `cube` and how to use it as an -application to run an `instance`. - -.. toctree:: - :maxdepth: 2 - - blog-in-five-minutes - create-cube - components - maintemplate - conclusion diff -r f7d2df59231a -r 16461f675734 doc/book/en/intro/tutorial/maintemplate.rst --- a/doc/book/en/intro/tutorial/maintemplate.rst Thu Apr 15 12:47:02 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -.. -*- coding: utf-8 -*- - -Templates ---------- - -Look at ``cubicweb/web/views/basetemplates.py`` and you will -find the base templates used to generate HTML for your application. - -A page is composed as indicated on the schema below: - -.. image:: ../../images/lax-book.06-main-template-layout.en.png - -In this section we will demonstrate a change in one of the main -interesting template from the three you will look for, -that is to say, the HTMLPageHeader, the HTMLPageFooter -and the TheMainTemplate. - - -Customize a template -~~~~~~~~~~~~~~~~~~~~ - -Based on the diagram below, each template can be overriden -by your customized template. To do so, we recommand you create -a Python module ``blog.views.templates`` to keep it organized. -In this module you will have to import the parent class you are -interested as follows: :: - - from cubicweb.web.views.basetemplates import HTMLPageHeader, \ - HTMLPageFooter, TheMainTemplate - -and then create your sub-class:: - - class MyBlogHTMLPageHeader(HTMLPageHeader): - ... - -Customize header -````````````````` - -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 user interface. - -Let's say we do not want anymore the login menu to be in the header - -First, to remove the login menu, we just need to comment out the display of the -login graphic component such as follows: - -.. sourcecode:: python - - class MyBlogHTMLPageHeader(HTMLPageHeader): - - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - self.w(u'
      \n') - self.w(u'\n') - # appliname and breadcrumbs - self.w(u'') - # logged user and help - #self.w(u'') - # lastcolumn - self.w(u'\n') - self.w(u'\n') - self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', - title=False, message=False) - - - -.. image:: ../../images/lax-book.06-header-no-login.en.png - -Customize footer -```````````````` - -If you want to change the footer for example, look -for HTMLPageFooter and override it in your views file as in: - -.. sourcecode:: python - - from cubicweb.web.views.basetemplates import HTMLPageFooter - - class MyHTMLPageFooter(HTMLPageFooter): - - def call(self, **kwargs): - self.w(u'') - -Updating a view does not require any restart of the server. By reloading -the page you can see your new page footer. - - -TheMainTemplate -``````````````` - -.. _TheMainTemplate: - -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. - -TheMainTemplate is responsible for the general layout of the entire application. -It defines the template of ``__regid__ = main`` that is used by the application. Is -also defined in ``cubicweb/web/views/basetemplates.py`` another template that can -be used based on TheMainTemplate called SimpleMainTemplate which does not have -a top section. - -.. image:: ../../images/lax-book.06-simple-main-template.en.png - -XXX -[WRITE ME] - -* customize MainTemplate and show that everything in the user - interface can be changed - diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/layout.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/standard_theme/layout.html Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,13 @@ +{% extends "basic/layout.html" %} + +{% block header %} + +{% endblock %} + +{# puts the sidebar into "sidebar1" block i.e. before the document body #} +{% block sidebar1 %}{{ sidebar() }}{% endblock %} +{% block sidebar2 %}{% endblock %} diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/static/contents.png Binary file doc/book/en/standard_theme/static/contents.png has changed diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/static/lglb-sphinx-doc.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/standard_theme/static/lglb-sphinx-doc.css Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,376 @@ +/** + * Sphinx stylesheet -- Logilab theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Inspired from sphinxdoc original theme. + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: '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; + color: #000000; + padding: 0; + border: 1px solid #CCBCA7; + min-width: 640px; + margin: 0px 30px 0px 30px; +} + +div.document { + background-color: #FFFFFF; + text-align: left; + background-image: url(contents.png); + background-repeat: repeat-x; +} + +div.bodywrapper { + margin: 0 0 0 230px; + border-left: 1px solid #CCBCA7; +} + +div.body { + margin: 0; + padding: 0.5em 20px 20px 20px; +} + +div.header { + margin: 0; + padding: 5px 5px 25px 5px; + background-color: #FFFFFF; + text-align: left; + line-height: 80%; + } + +div.related { + font-size: 1em; +} + +div.related ul { + background-image: url(navigation.png); + height: 2em; + border-top: 1px solid #CCBCA7; + border-bottom: 1px solid #CCBCA7; +} + +div.related ul li { + margin: 0; + padding: 0; + height: 2em; + float: left; +} + +div.related ul li.right { + float: right; + margin-right: 5px; +} + +div.related ul li a { + margin: 0; + padding: 0 5px 0 5px; + line-height: 1.75em; +} + +div.sphinxsidebarwrapper { + padding: 0; +} + +div.sphinxsidebar { + margin: 0; + padding: 5px 10px 5px 10px; + width: 210px; + float: left; + font-size: 1em; + text-align: left; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin: 1em 0 0.5em 0; + font-size: 1em; + padding: 0.1em 0 0.1em 0.5em; + color: white; + border: 1px solid #CCBCA7; + background-color: #909090; +} + +div.sphinxsidebar h3 a { + color: white; +} + +div.sphinxsidebar ul { + padding-left: 1.5em; + margin-top: 7px; + padding: 0; + line-height: 130%; + font-weight: bold; +} + +div.sphinxsidebar ul ul { + margin-left: 20px; + font-weight: normal; +} + +div.sphinxsidebar li { + margin: 0; +} + +div.sphinxsidebar input { + border: 1px solid #CCBCA7; + font-family: sans-serif; + font-size: 1em; +} + +div.footer { + background-color: #FFFFFF; + color: #707070; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; +} + +div.footer a { + color: #707070; + text-decoration: underline; +} + +/* -- body styles ----------------------------------------------------------- */ + +p { + margin: 0.8em 0 0 0; +} + +ul, ol { + margin: 0; +} + +li { + margin: 0.2em 0 0 0; +} + +a { + color: #B45300; + text-decoration: none; +} + +a:hover { + color: #4BACFF; +} + +div.body a { + text-decoration: underline; +} + +h1 { + margin: 0; + padding: 0.7em 0 0.3em 0; + font-size: 1.5em; + border-bottom: 3px solid #CC8B00; + color: #404040; +} + +h2 { + margin: 1.3em 0 0.2em 0; + font-size: 1.35em; + padding: 0; + color: #303030; +} + +h3 { + margin: 1em 0 -0.3em 0; + font-size: 1.2em; + color: #202020; +} + +div.body h1 a { + color: #404040!important; +} + +div.body h2 a { + color: #303030!important; +} + +div.body h3 a { + color: #202020!important; +} + +div.body h4 a, div.body h5 a, div.body h6 a { + color: #000000!important; +} + +h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { + display: none; + margin: 0 0 0 0.3em; + padding: 0 0.2em 0 0.2em; + color: #AAA!important; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, +h5:hover a.anchor, h6:hover a.anchor { + display: inline; +} + +h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, +h5 a.anchor:hover, h6 a.anchor:hover { + color: #777; + background-color: #EEE; +} + +a.headerlink { + color: #C60F0F!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; +} + +a.headerlink:hover { + background-color: #C0C0C0; + color: #FFFFFF!important; +} + +cite, code, tt { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.01em; +} + +tt { + background-color: #F0F0F0; + border-bottom: 1px solid #D0D0D0; +} + +tt.descname, tt.descclassname, tt.xref { + background-color: #F0F0F0; + font-weight: normal; + font-size: 1em; + border: 1px solid #D0D0D0; + border: 0; +} + +hr { + border: 1px solid #CC8B00; + margin: 2em; +} + +a tt { + border: 0; + color: #B45300; +} + +a:hover tt { + color: #4BACFF; +} + +pre { + font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; + font-size: 0.95em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.5em; + border: 1px solid #CCBCA7; + background-color: #F0F0F0; +} + +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #F8F8F8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #C0C0C0; +} + +div.topic { + background-color: #F8F8F8; +} + +table { + border-collapse: collapse; + margin: 0.8em -0.5em 0em -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; +} + +div.admonition, div.warning { + font-size: 0.9em; + margin: 1em 0 1em 0; + padding: 0; +} + +div.admonition { + border: 1px solid #86989B; + background-color: #EBEBFF; +} + +div.warning { + border: 1px solid #940000; + background-color: #FFEBEB; +} + +div.admonition p, div.warning p { + margin: 0.5em 1em 0.5em 1em; + padding: 0; +} + +div.admonition pre, div.warning pre { + margin: 0.4em 1em 0.4em 1em; +} + +div.admonition p.admonition-title, +div.warning p.admonition-title { + margin: 0; + padding: 0.1em 0 0.1em 0.5em; + color: #FFFFFF; + font-weight: bold; +} + +div.admonition p.admonition-title { + border-bottom: 1px solid #86989B; + background-color: #8C88B5; +} + +div.warning p.admonition-title { + background-color: #CF0000; + border-bottom: 1px solid #940000; +} + +div.admonition ul, div.admonition ol, +div.warning ul, div.warning ol { + margin: 0.1em 0.5em 0.5em 3em; + padding: 0; +} + +div.versioninfo { + margin: 1em 0 0 0; + border: 1px solid #C0C0C0; + background-color: #DDEAF0; + padding: 8px; + line-height: 1.3em; + font-size: 0.9em; +} + +/* TOC trees */ + +li.toctree-l1 { + margin-top: 0.4em; + } \ No newline at end of file diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/static/logilab_logo.png Binary file doc/book/en/standard_theme/static/logilab_logo.png has changed diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/static/navigation.png Binary file doc/book/en/standard_theme/static/navigation.png has changed diff -r f7d2df59231a -r 16461f675734 doc/book/en/standard_theme/theme.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/standard_theme/theme.conf Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = lglb-sphinx-doc.css +pygments_style = friendly diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/advanced/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/advanced/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,586 @@ +.. _advanced_tutorial: + +Building a photo gallery with CubicWeb +====================================== + +Desired features +---------------- + +* basically a photo gallery + +* photo stored onto the fs and displayed dynamically through a web interface + +* navigation through folder (album), tags, geographical zone, people on the + picture... using facets + +* advanced security (eg not everyone can see everything). More on this later. + + +Cube creation and schema definition +----------------------------------- + +.. _adv_tuto_create_new_cube: + +Step 1: creating a new cube for my web site +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One note about my development environment: I wanted to use packaged +version of CubicWeb and cubes while keeping my cube in my user +directory, let's say `~src/cubes`. I achieve this by setting the +following environment variables:: + + CW_CUBES_PATH=~/src/cubes + CW_MODE=user + +I can now create the cube which will hold custom code for this web +site using:: + + c-c newcube --directory=~/src/cubes sytweb + + +.. _adv_tuto_assemble_cubes: + +Step 2: pick building blocks into existing cubes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Almost everything I want represent in my web-site is somewhat already modelized in +some cube that I'll extend for my need. So I'll pick the following cubes: + +* `folder`, containing `Folder` entity type, which will be used as + both 'album' and a way to map file system folders. Entities are + added to a given folder using the `filed_under` relation. + +* `file`, containing `File` and `Image` entity types, gallery view, + and a file system import utility. + +* `zone`, containing the `Zone` entity type for hierarchical geographical + zones. Entities (including sub-zones) are added to a given zone using the + `situated_in` relation. + +* `person`, containing the `Person` entity type plus some basic views. + +* `comment`, providing a full commenting system allowing one to comment entity types + supporting the `comments` relation by adding a `Comment` entity. + +* `tag`, providing a full tagging system as a easy and powerful way to classify + entities supporting the `tags` relation by linking the to `Tag` entities. This + will allows navigation into a large number of picture. + +Ok, now I'll tell my cube requires all this by editing cubes/sytweb/__pkginfo__.py: + + .. sourcecode:: python + + __depends_cubes__ = {'file': '>= 1.2.0', + 'folder': '>= 1.1.0', + 'person': '>= 1.2.0', + 'comment': '>= 1.2.0', + 'tag': '>= 1.2.0', + 'zone': None, + } + __depends__ = {'cubicweb': '>= 3.5.10', + } + for key,value in __depends_cubes__.items(): + __depends__['cubicweb-'+key] = value + __use__ = tuple(__depends_cubes__) + +Notice that you can express minimal version of the cube that should be used, +`None` meaning whatever version available. + +Step 3: glue everything together in my cube's schema +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. sourcecode:: python + + from yams.buildobjs import RelationDefinition + + class comments(RelationDefinition): + subject = 'Comment' + object = ('File', 'Image') + cardinality = '1*' + composite = 'object' + + class tags(RelationDefinition): + subject = 'Tag' + object = ('File', 'Image') + + class filed_under(RelationDefinition): + subject = ('File', 'Image') + object = 'Folder' + + class situated_in(RelationDefinition): + subject = 'Image' + object = 'Zone' + + class displayed_on(RelationDefinition): + subject = 'Person' + object = 'Image' + + +This schema: + +* allows to comment and tag on `File` and `Image` entity types by adding the + `comments` and `tags` relations. This should be all we've to do for this + feature since the related cubes provide 'pluggable section' which are + automatically displayed on the primary view of entity types supporting the + relation. + +* adds a `situated_in` relation definition so that image entities can be + geolocalized. + +* add a new relation `displayed_on` relation telling who can be seen on a + picture. + +This schema will probably have to evolve as time goes (for security handling at +least), but since the possibility to make schema evolving is one of CubicWeb +feature (and goal), we won't worry and see that later when needed. + + +Step 4: creating the instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that I've a schema, I want to create an instance so I can start To +create an instance using this new 'sytweb' cube, I run:: + + c-c create sytweb sytweb_instance + +hint : if you get an error while the database is initialized, you can +avoid having to reanswer to questions by runing :: + + c-c db-create sytweb_instance + +This will use your already configured instance and start directly from the create +database step, thus skipping questions asked by the 'create' command. + +Once the instance and database are fully initialized, run :: + + c-c start sytweb_instance + +to start the instance, check you can connect on it, etc... + + +Security, testing and migration +------------------------------- + +This post will cover various topics: + +* configuring security +* migrating existing instance +* writing some unit tests + +Here is the ``read`` security model I want: + +* folders, files, images and comments should have one of the following visibility: + - ``public``, everyone can see it + - ``authenticated``, only authenticated users can see it + - ``restricted``, only a subset of authenticated users can see it +* managers (e.g. me) can see everything +* only authenticated user can see people +* everyone can see classifier entities, eg tag and zone + +Also, unless explicity specified, visibility of an image should be the same as +its parent folder, as well as visibility of a comment should be the same as the +commented entity. If there is no parent entity, the default visibility is +``authenticated``. + +Regarding write security, that's much easier: +* anonymous can't write anything +* authenticated users can only add comment +* managers will add the remaining stuff + +Now, let's implement that! + +Proper security in CubicWeb is done at the schema level, so you don't have to +bother with it in views: users will only see what they can see automatically. + +.. _adv_tuto_security: + +Step 1: configuring security into the schema +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In schema, you can grant access according to groups, or to some RQL expressions: +users get access it the expression return some results. To implements the read +security defined earlier, groups are not enough, we'll need RQL expression. Here +is the idea: + +* add a `visibility` attribute on folder, image and comment, which may be one of + the value explained above + +* add a `may_be_read_by` relation from folder, image and comment to users, + which will define who can see the entity + +* security propagation will be done in hook. + +So the first thing to do is to modify my cube'schema.py to define those +relations: + +.. sourcecode:: python + + from yams.constraints import StaticVocabularyConstraint + + class visibility(RelationDefinition): + subject = ('Folder', 'File', 'Image', 'Comment') + object = 'String' + constraints = [StaticVocabularyConstraint(('public', 'authenticated', + 'restricted', 'parent'))] + default = 'parent' + cardinality = '11' # required + + class may_be_read_by(RelationDefinition): + subject = ('Folder', 'File', 'Image', 'Comment',) + object = 'CWUser' + +We can note the following points: + +* we've added a new `visibility` attribute to folder, file, image and comment + using a `RelationDefinition` + +* `cardinality = '11'` means this attribute is required. This is usually hidden + under the `required` argument given to the `String` constructor, but we can + rely on this here (same thing for StaticVocabularyConstraint, which is usually + hidden by the `vocabulary` argument) + +* the `parent` possible value will be used for visibility propagation + +Now, we should be able to define security rules in the schema, based on these new +attribute and relation. Here is the code to add to *schema.py*: + +.. sourcecode:: python + + from cubicweb.schema import ERQLExpression + + VISIBILITY_PERMISSIONS = { + 'read': ('managers', + ERQLExpression('X visibility "public"'), + ERQLExpression('X may_be_read_by U')), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + AUTH_ONLY_PERMISSIONS = { + 'read': ('managers', 'users'), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + CLASSIFIERS_PERMISSIONS = { + 'read': ('managers', 'users', 'guests'), + 'add': ('managers',), + 'update': ('managers', 'owners',), + 'delete': ('managers', 'owners'), + } + + from cubes.folder.schema import Folder + from cubes.file.schema import File, Image + from cubes.comment.schema import Comment + from cubes.person.schema import Person + from cubes.zone.schema import Zone + from cubes.tag.schema import Tag + + Folder.__permissions__ = VISIBILITY_PERMISSIONS + File.__permissions__ = VISIBILITY_PERMISSIONS + Image.__permissions__ = VISIBILITY_PERMISSIONS + Comment.__permissions__ = VISIBILITY_PERMISSIONS.copy() + Comment.__permissions__['add'] = ('managers', 'users',) + Person.__permissions__ = AUTH_ONLY_PERMISSIONS + Zone.__permissions__ = CLASSIFIERS_PERMISSIONS + Tag.__permissions__ = CLASSIFIERS_PERMISSIONS + +What's important in there: + +* `VISIBILITY_PERMISSIONS` provides read access to managers group, if + `visibility` attribute's value is 'public', or if user (designed by the 'U' + variable in the expression) is linked to the entity (the 'X' variable) through + the `may_read` permission + +* we modify permissions of the entity types we use by importing them and + modifying their `__permissions__` attribute + +* notice the `.copy()`: we only want to modify 'add' permission for `Comment`, + not for all entity types using `VISIBILITY_PERMISSIONS`! + +* the remaining part of the security model is done using regular groups: + + - `users` is the group to which all authenticated users will belong + - `guests` is the group of anonymous users + + +.. _adv_tuto_security_propagation: + +Step 2: security propagation in hooks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To fullfill the requirements, we have to implement:: + + Also, unless explicity specified, visibility of an image should be the same as + its parent folder, as well as visibility of a comment should be the same as the + commented entity. + +This kind of `active` rule will be done using CubicWeb's hook +system. Hooks are triggered on database event such as addition of new +entity or relation. + +The trick part of the requirement is in *unless explicitly specified*, notably +because when the entity addition hook is added, we don't know yet its 'parent' +entity (eg folder of an image, image commented by a comment). To handle such things, +CubicWeb provides `Operation`, which allow to schedule things to do at commit time. + +In our case we will: + +* on entity creation, schedule an operation that will set default visibility + +* when a "parent" relation is added, propagate parent's visibility unless the + child already has a visibility set + +Here is the code in cube's *hooks.py*: + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.server import hook + + class SetVisibilityOp(hook.Operation): + def precommit_event(self): + for eid in self.session.transaction_data.pop('pending_visibility'): + entity = self.session.entity_from_eid(eid) + if entity.visibility == 'parent': + entity.set_attributes(visibility=u'authenticated') + + class SetVisibilityHook(hook.Hook): + __regid__ = 'sytweb.setvisibility' + __select__ = hook.Hook.__select__ & implements('Folder', 'File', 'Image', 'Comment') + events = ('after_add_entity',) + def __call__(self): + hook.set_operation(self._cw, 'pending_visibility', self.entity.eid, + SetVisibilityOp) + + class SetParentVisibilityHook(hook.Hook): + __regid__ = 'sytweb.setparentvisibility' + __select__ = hook.Hook.__select__ & hook.match_rtype('filed_under', 'comments') + events = ('after_add_relation',) + + def __call__(self): + parent = self._cw.entity_from_eid(self.eidto) + child = self._cw.entity_from_eid(self.eidfrom) + if child.visibility == 'parent': + child.set_attributes(visibility=parent.visibility) + +Notice: + +* hooks are application objects, hence have selectors that should match entity or + relation types to which the hook applies. To match a relation type, we use the + hook specific `match_rtype` selector. + +* usage of `set_operation`: instead of adding an operation for each added entity, + set_operation allows to create a single one and to store entity's eids to be + processed in session's transaction data. This is a good pratice to avoid heavy + operations manipulation cost when creating a lot of entities in the same + transaction. + +* the `precommit_event` method of the operation will be called at transaction's + commit time. + +* in a hook, `self._cw` is the repository session, not a web request as usually + in views + +* according to hook's event, you have access to different attributes on the hook + instance. Here: + + - `self.entity` is the newly added entity on 'after_add_entity' events + + - `self.eidfrom` / `self.eidto` are the eid of the subject / object entity on + 'after_add_relatiohn' events (you may also get the relation type using + `self.rtype`) + +The `parent` visibility value is used to tell "propagate using parent security" +because we want that attribute to be required, so we can't use None value else +we'll get an error before we get any chance to propagate... + +Now, we also want to propagate the `may_be_read_by` relation. Fortunately, +CubicWeb provides some base hook classes for such things, so we only have to add +the following code to *hooks.py*: + +.. sourcecode:: python + + # relations where the "parent" entity is the subject + S_RELS = set() + # relations where the "parent" entity is the object + O_RELS = set(('filed_under', 'comments',)) + + class AddEntitySecurityPropagationHook(hook.PropagateSubjectRelationHook): + """propagate permissions when new entity are added""" + __regid__ = 'sytweb.addentity_security_propagation' + __select__ = (hook.PropagateSubjectRelationHook.__select__ + & hook.match_rtype_sets(S_RELS, O_RELS)) + main_rtype = 'may_be_read_by' + subject_relations = S_RELS + object_relations = O_RELS + + class AddPermissionSecurityPropagationHook(hook.PropagateSubjectRelationAddHook): + """propagate permissions when new entity are added""" + __regid__ = 'sytweb.addperm_security_propagation' + __select__ = (hook.PropagateSubjectRelationAddHook.__select__ + & hook.match_rtype('may_be_read_by',)) + subject_relations = S_RELS + object_relations = O_RELS + + class DelPermissionSecurityPropagationHook(hook.PropagateSubjectRelationDelHook): + __regid__ = 'sytweb.delperm_security_propagation' + __select__ = (hook.PropagateSubjectRelationDelHook.__select__ + & hook.match_rtype('may_be_read_by',)) + subject_relations = S_RELS + object_relations = O_RELS + +* the `AddEntitySecurityPropagationHook` will propagate the relation + when `filed_under` or `comments` relations are added + + - the `S_RELS` and `O_RELS` set as well as the `match_rtype_sets` selector are + used here so that if my cube is used by another one, it'll be able to + configure security propagation by simply adding relation to one of the two + sets. + +* the two others will propagate permissions changes on parent entities to + children entities + + +.. _adv_tuto_tesing_security: + +Step 3: testing our security +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Security is tricky. Writing some tests for it is a very good idea. You should +even write them first, as Test Driven Development recommends! + +Here is a small test case that will check the basis of our security +model, in *test/unittest_sytweb.py*: + +.. sourcecode:: python + + from cubicweb.devtools.testlib import CubicWebTC + from cubicweb import Binary + + class SecurityTC(CubicWebTC): + + def test_visibility_propagation(self): + # create a user for later security checks + toto = self.create_user('toto') + # init some data using the default manager connection + req = self.request() + folder = req.create_entity('Folder', + name=u'restricted', + visibility=u'restricted') + photo1 = req.create_entity('Image', + data_name=u'photo1.jpg', + data=Binary('xxx'), + filed_under=folder) + self.commit() + photo1.clear_all_caches() # good practice, avoid request cache effects + # visibility propagation + self.assertEquals(photo1.visibility, 'restricted') + # unless explicitly specified + photo2 = req.create_entity('Image', + data_name=u'photo2.jpg', + data=Binary('xxx'), + visibility=u'public', + filed_under=folder) + self.commit() + self.assertEquals(photo2.visibility, 'public') + # test security + self.login('toto') + req = self.request() + self.assertEquals(len(req.execute('Image X')), 1) # only the public one + self.assertEquals(len(req.execute('Folder X')), 0) # restricted... + # may_be_read_by propagation + self.restore_connection() + folder.set_relations(may_be_read_by=toto) + self.commit() + photo1.clear_all_caches() + self.failUnless(photo1.may_be_read_by) + # test security with permissions + self.login('toto') + req = self.request() + self.assertEquals(len(req.execute('Image X')), 2) # now toto has access to photo2 + self.assertEquals(len(req.execute('Folder X')), 1) # and to restricted folder + + if __name__ == '__main__': + from logilab.common.testlib import unittest_main + unittest_main() + +It's not complete, but show most things you'll want to do in tests: adding some +content, creating users and connecting as them in the test, etc... + +To run it type: :: + + [syt@scorpius test]$ pytest unittest_sytweb.py + ======================== unittest_sytweb.py ======================== + -> creating tables [....................] + -> inserting default user and default groups. + -> storing the schema in the database [....................] + -> database for instance data initialized. + . + ---------------------------------------------------------------------- + Ran 1 test in 22.547s + + OK + + +The first execution is taking time, since it creates a sqlite database for the +test instance. The second one will be much quicker: :: + + [syt@scorpius test]$ pytest unittest_sytweb.py + ======================== unittest_sytweb.py ======================== + . + ---------------------------------------------------------------------- + Ran 1 test in 2.662s + + OK + +If you do some changes in your schema, you'll have to force regeneration of that +database. You do that by removing the tmpdb files before running the test: :: + + [syt@scorpius test]$ rm tmpdb* + + +.. Note:: + pytest is a very convenient utilities to control test execution, from the `logilab-common`_ + package + +.. _`logilab-common`: http://www.logilab.org/project/logilab-common + +.. _adv_tuto_migration_script: + +Step 4: writing the migration script and migrating the instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prior to those changes, Iv'e created an instance, feeded it with some data, so I +don't want to create a new one, but to migrate the existing one. Let's see how to +do that. + +Migration commands should be put in the cube's *migration* directory, in a +file named file:`_Any.py` ('Any' being there mostly for historical reason). + +Here I'll create a *migration/0.2.0_Any.py* file containing the following +instructions: + +.. sourcecode:: python + + add_relation_type('may_be_read_by') + add_relation_type('visibility') + sync_schema_props_perms() + +Then I update the version number in cube's *__pkginfo__.py* to 0.2.0. And +that's it! Those instructions will: + +* update the instance's schema by adding our two new relations and update the + underlying database tables accordingly (the two first instructions) + +* update schema's permissions definition (the later instruction) + + +To migrate my instance I simply type:: + + [syt@scorpius ~]$ cubicweb-ctl upgrade sytweb + +I'll then be asked some questions to do the migration step by step. You should say +YES when it asks if a backup of your database should be done, so you can get back +to initial state if anything goes wrong... + diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/blog-in-five-minutes.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/blog-in-five-minutes.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,42 @@ +.. -*- coding: utf-8 -*- + +.. _BlogFiveMinutes: + +Get a blog running in five minutes! +----------------------------------- + +For Debian or Ubuntu users, first install the following packages (:ref:`DebianInstallation`):: + + cubicweb, cubicweb-dev, cubicweb-blog + +For Windows or Mac OS X users, you must install cubicweb from source (see :ref:`SourceInstallation` and :ref:`WindowsInstallation`). + +Then create and initialize your instance:: + + cubicweb-ctl create blog myblog + +And start it:: + + cubicweb-ctl start -D myblog + +The -D option is the debugging mode of cubicweb, removing it will lauch the instance in the background. + +Permission +~~~~~~~~~~ + +This command assumes that you have root access to the /etc/ path. In order to initialize your instance as a `user` (from scratch), please check your current PYTHONPATH then create the ~/etc/cubicweb.d directory. + +Instance parameters +~~~~~~~~~~~~~~~~~~~ + +If the database installation failed, you'd like to change some instance parameters, for example, the database host or the user name. These informations can be edited in the `source` file located in the /etc/cubicweb.d/myblog directory. + +Then relaunch the database creation: + + cubicweb-ctl db-create myblog + +Other paramaters, like web server or emails parameters, can be modified in the `all-in-one.conf` file. + +This is it. Your blog is running. Visit http://localhost:8080 and enjoy it! This blog is fully functionnal. The next section section will present the way to develop new cubes and customizing the look of your instance. + + diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/components.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/components.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,79 @@ +.. -*- coding: utf-8 -*- + +.. _cubes: + +Cubes +----- + +Standard library +~~~~~~~~~~~~~~~~ + +A library of standard cubes are available from `CubicWeb Forge`_ +Cubes provide entities and views. + +The available application entities in standard cubes are: + +* addressbook: PhoneNumber and PostalAddress + +* basket: Basket (like a shopping cart) + +* blog: Blog (a *very* basic blog) + +* classfolder: Folder (to organize things but grouping them in folders) + +* classtags: Tag (to tag anything) + +* comment: Comment (to attach comment threads to entities) + +* file: File (to allow users to upload and store binary or text files) + +* link: Link (to collect links to web resources) + +* mailinglist: MailingList (to reference a mailing-list and the URLs + for its archives and its admin interface) + +* person: Person (easily mixed with addressbook) + +* task: Task (something to be done between start and stop date) + +* zone: Zone (to define places within larger places, for example a + city in a state in a country) + +.. _`CubicWeb Forge`: http://www.cubicweb.org/project/ + +Adding comments to BlogDemo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To import a cube in your instance just change the line in the +``__pkginfo__.py`` file and verify that the cube you are planning +to use is listed by the command ``cubicweb-ctl list``. +For example:: + + __use__ = ('comment',) + +will make the ``Comment`` entity available in your ``BlogDemo`` +cube. + +Change the schema to add a relationship between ``BlogEntry`` and +``Comment`` and you are done. Since the comment cube defines the +``comments`` relationship, adding the line:: + + comments = ObjectRelation('Comment', cardinality='1*', composite='object') + +to the definition of a ``BlogEntry`` will be enough. + +Synchronize the data model +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you modified your data model, you need to synchronize the +database with your model. For this purpose, *CubicWeb* provides +a very useful command ``cubicweb-ctl shell blogdemo`` which +launches an interactive shell where you can enter migration +commands (see :ref:`cubicweb-ctl` for more details)). +As you added the cube named `comment`, you need to run: + +:: + + add_cube('comment') + +You can now start your instance and comment your blog entries. diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/conclusion.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/conclusion.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,15 @@ +.. -*- coding: utf-8 -*- + +What's next? +------------ + +We demonstrated how from a straight out of the box *CubicWeb* installation, you +can build your web application based on a data model. It's all already there: +views, templates, permissions, etc. The step forward is now for you to customize +according to your needs. + +Many features are available to extend your application, for example: RSS channel +integration (:ref:`XmlAndRss`), hooks (:ref:`hooks`), support of sources such as +Google App Engine (:ref:`GoogleAppEngineSource`) and lots of others to discover +through our book. + diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/create-cube.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/create-cube.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,431 @@ +.. -*- coding: utf-8 -*- + +.. _Steps: + +Steps for creating your cube +---------------------------- + +The following steps will help you to create and customize a new cube. + +1. :ref:`CreateYourCube` + +Create the directory to hold the code of your cube. The most important +files that will be useful to customize your newly created cube are: + + * schema.py: contains the data model + * views.py: contains your custom views + * entities.py: contains XXX + +The detailed structure of the code directory is described in :ref:`cubelayout`. + +2. :ref:`DefineDataModel` + +Define the data model of your application. + +3. :ref:`ExploreYourInstance` + +Create, run, and explore an instance of your cube. + +4. :ref:`DefineViews` + +Customize the views of your data: how and which part of your data are showed. + +Note: views don't concern the look'n'feel or design of the site. For that, you should use CSS instead, and default CSS or your new cube are located in 'blog/data/'. + + +5. :ref:`DefineEntities` + +Define your own entities to add useful functions when you manipulate your data, especially when you write view. + + +.. _CreateYourCube: + +Create your cube +---------------- + +The packages ``cubicweb`` and ``cubicweb-dev`` install a command line +tool for *CubicWeb* called ``cubicweb-ctl``. This tool provides a wide +range of commands described in details in :ref:`cubicweb-ctl`. + +Once your *CubicWeb* development environment is set up, you can create +a new cube:: + + cubicweb-ctl newcube blog + +This will create in the cubes directory (``/path/to/forest/cubes`` for Mercurial +installation, ``/usr/share/cubicweb/cubes`` for debian packages installation) +a directory named ``blog`` reflecting the structure described in :ref:`Concepts`. + + +For packages installation, you can still create new cubes in your home directory using the following configuration. Let's say you want to develop your new cubes in `~src/cubes`, then set the following environment variables: +:: + + CW_CUBES_PATH=~/src/cubes + CW_MODE=user + +and then create your new cube using: +:: + + cubicweb-ctl newcube --directory=~/src/cubes blog + + + + + + +.. _DefineDataModel: + +Define your data model +---------------------- + +The data model or schema is the core of your *CubicWeb* application. +It defines the type of content your application will handle. + +The data model of your cube ``blog`` is defined in the file ``schema.py``: + +.. sourcecode:: python + + from yams.buildobjs import EntityType, String, SubjectRelation, Date + + class Blog(EntityType): + title = String(maxsize=50, required=True) + description = String() + + class BlogEntry(EntityType): + title = String(required=True, fulltextindexed=True, maxsize=256) + publish_date = Date(default='TODAY') + content = String(required=True, fulltextindexed=True) + entry_of = SubjectRelation('Blog', cardinality='?*') + +The first step is the import of the EntityType (generic class for entity and +attributes that will be used in both Blog and BlogEntry entities. + +A Blog has a title and a description. The title is a string that is +required and must be less than 50 characters. The +description is a string that is not constrained. + +A BlogEntry has a title, a publish_date and a content. The title is a +string that is required and must be less than 100 characters. The +publish_date is a Date with a default value of TODAY, meaning that +when a BlogEntry is created, its publish_date will be the current day +unless it is modified. The content is a string that will be indexed in +the database full-text index and has no constraint. + +A BlogEntry also has a relationship ``entry_of`` that links it to a +Blog. The cardinality ``?*`` means that a BlogEntry can be part of +zero or one Blog (``?`` means `zero or one`) and that a Blog can +have any number of BlogEntry (``*`` means `any number including +zero`). For completeness, remember that ``+`` means `one or more`. + + +.. _ExploreYourInstance: + +Create and explore your instance +-------------------------------- +.. _CreateYourInstance: + +Create your instance +~~~~~~~~~~~~~~~~~~~~ + +To use this cube as an instance and create a new instance named ``blogdemo``, do:: + + cubicweb-ctl create blog blogdemo + +This command will create the corresponding database and initialize it. + + +.. _WelcomeToYourWebInstance: + +Welcome to your web instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Start your instance in debug mode with the following command: :: + + cubicweb-ctl start -D blogdemo + + +You can now access your web instance to create blogs and post messages +by visiting the URL http://localhost:8080/. + +A login form will appear. By default, the instance will not allow anonymous +users to enter the instance. To login, you need then use the admin account +you created at the time you initialized the database with ``cubicweb-ctl +create``. + +.. image:: ../../images/login-form.png + + +Once authenticated, you can start playing with your instance +and create entities. + +.. image:: ../../images/blog-demo-first-page.png + +Please notice that so far, the *CubicWeb* framework managed all aspects of +the web application based on the schema provided at the beginning. + +.. _AddEntities: + +Add entities +~~~~~~~~~~~~ + +We will now add entities in our web application. + +Add a Blog +********** + +Let us create a few of these entities. Click on the `[+]` at the left of the +link Blog on the home page. Call this new Blog ``Tech-blog`` and type in +``everything about technology`` as the description, then validate the form by +clicking on ``Validate``. + +.. image:: ../../images/cbw-create-blog.en.png + :alt: from to create blog + +Click on the logo at top left to get back to the home page, then +follow the Blog link that will list for you all the existing Blog. +You should be seeing a list with a single item ``Tech-blog`` you +just created. + +.. image:: ../../images/cbw-list-one-blog.en.png + :alt: displaying a list of a single blog + +Clicking on this item will get you to its detailed description except +that in this case, there is not much to display besides the name and +the phrase ``everything about technology``. + +Now get back to the home page by clicking on the top-left logo, then +create a new Blog called ``MyLife`` and get back to the home page +again to follow the Blog link for the second time. The list now +has two items. + +.. image:: ../../images/cbw-list-two-blog.en.png + :alt: displaying a list of two blogs + +Add a BlogEntry +*************** + +Get back to the home page and click on [+] at the left of the link +BlogEntry. Call this new entry ``Hello World`` and type in some text +before clicking on ``Validate``. You added a new blog entry without +saying to what blog it belongs. There is a box on the left entitled +``actions``, click on the menu item ``modify``. You are back to the form +to edit the blog entry you just created, except that the form now has +another section with a combobox titled ``add relation``. Chose +``entry_of`` in this menu and a second combobox appears where you pick +``MyLife``. + +You could also have, at the time you started to fill the form for a +new entity BlogEntry, hit ``Apply`` instead of ``Validate`` and the +combobox titled ``add relation`` would have showed up. + + +.. image:: ../../images/cbw-add-relation-entryof.en.png + :alt: editing a blog entry to add a relation to a blog + +Validate the changes by clicking ``Validate``. The entity BlogEntry +that is displayed now includes a link to the entity Blog named +``MyLife``. + +.. image:: ../../images/cbw-detail-one-blogentry.en.png + :alt: displaying the detailed view of a blogentry + +Note that all of this was handled by the framework and that the only input +that was provided so far is the schema. To get a graphical view of the schema, +point your browser to the URL http://localhost:8080/schema + +.. image:: ../../images/cbw-schema.en.png + :alt: graphical view of the schema (aka data-model) + + +.. _DefineViews: + +Define your entity views +------------------------ + +Each entity defined in a model is associated with default views +allowing different renderings of the data. You can redefine each of +them according to your needs and preferences. So let's see how the +views are defined. + + +The view selection principle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A view is defined by a Python class which includes: + + - an identifier (all objects in *CubicWeb* are recorded in a + registry and this identifier will be used as a key) + + - a filter to select the result sets it can be applied to + +A view has a set of methods complying with the `View` class interface +(`cubicweb.common.view`). + +*CubicWeb* provides a lot of standard views for the type `EntityView`; +for a complete list, read the code in directory ``cubicweb/web/views/``. + +A view is applied on a `result set` which contains a set of entities +we are trying to display. *CubicWeb* uses a selector mechanism which +computes for each available view a score: the view with the highest +score is then used to display the given `result set`. The standard +library of selectors is in ``cubicweb.selector``. + +It is possible to define multiple views for the same identifier +and to associate selectors and filters to allow the application +to find the most appropriate way to render the data. + +For example, the view named ``primary`` is the one used to display a +single entity. We will now show you how to create a primary view for +BlogEntry. + + +Primary view customization +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you wish to modify the way a `BlogEntry` is rendered, you will have +to subclass the `primary` view, for instance in the module ``views`` +of the cube ``cubes/blog/views.py``. + +The standard primary view is the most sophisticated view of all. It +has more than a call() method. It is a template. Actually the entry +point calls the following sequence of (redefinable) methods: + + * render_entity_title + + * render_entity_metadata + + * render_entity_attributes + + * render_entity_relations + + * render_side_boxes + +Excepted side boxes, we can see all of them already in action in the +blog entry view. This is all described in more details in +:ref:`primary`. + +We can for example add in front of the publication date a prefix +specifying that the date we see is the publication date. + +To do so, please apply the following changes: + +.. sourcecode:: python + + from cubicweb.selectors import implements + from cubicweb.web.views import primary + + class BlogEntryPrimaryView(primary.PrimaryView): + __select__ = implements('BlogEntry') + + def render_entity_attributes(self, entity): + self.w(u'

      published on %s

      ' % + entity.publish_date.strftime('%Y-%m-%d')) + super(BlogEntryPrimaryView, self).render_entity_attributes(entity) + +.. note:: + When a view is modified, it is not required to restart the instance + server. Save the Python file and reload the page in your web browser + to view the changes. + +You can now see that the publication date has a prefix. + +.. image:: ../../images/cbw-update-primary-view.en.png + :alt: modified primary view + + +The above source code defines a new primary view for ``BlogEntry``. + +Since views are applied to result sets and result sets can be tables of +data, we have to recover the entity from its (row,col)-coordinates. +The view has a ``self.w()`` method that is used to output data, in our +example HTML output. + +.. note:: + You can find more details about views and selectors in :ref:`Views`. + + +.. _DefineEntities: + +Write entities to add logic in your data +---------------------------------------- + +By default, CubicWeb provides a default entity for each data type defined in the schema. +A default entity mainly contains the attributes defined in the data model. + +You can redefine each entity to provide additional functions to help you write your views. + +.. sourcecode:: python + + from cubicweb.entities import AnyEntity + + class BlogEntry(AnyEntity): + """customized class for BlogEntry entities""" + __regid__ = 'BlogEntry' + __implements__ = AnyEntity.__implements__ + + def display_cw_logo(self): + if 'CW' in self.title: + return True + else: + return False + +Customizing an entity requires that your entity: + - inherits from ``cubicweb.entities`` or any subclass + - defines a ``__regid__`` linked to the corresponding data type of your schema + - implements the base class by explicitly using ``__implements__``. + +We implemented here a function ``display_cw_logo`` which tests if the blog entry title contains 'CW'. +This function can then be used when you customize your views. For instance, you can modify your previous ``views.py`` as follows: + +.. sourcecode:: python + + class BlogEntryPrimaryView(primary.PrimaryView): + __select__ = implements('BlogEntry') + + ... + + def render_entity_title(self, entity): + if entity.display_cw_logo(): + self.w(u'') + super(BlogEntryPrimaryView, self).render_entity_title(entity) + +Then each blog entry whose title contains 'CW' is shown with the CubicWeb logo in front of it. + +.. _UpdatingSchemaAndSynchronisingInstance: + +Updating the schema and synchronising the instance +-------------------------------------------------- + +While developping your cube, you may want to update your data model. Let's say you +want to add a ``category`` attribute in the ``Blog`` data type. This is called a migration. + +The required steps are: +1. modify the file ``schema.py``. The ``Blog`` class looks now like this: + +.. sourcecode:: python + + class Blog(EntityType): + title = String(maxsize=50, required=True) + description = String() + category = String(required=True, vocabulary=(_('Professional'), _('Personal')), default='Personal') + +2. stop your ``blogdemo`` instance + +3. start the cubicweb shell for your instance by running the following command: + +.. sourcecode:: bash + + cubicweb-ctl shell blogdemo + +4. in the shell, execute: + +.. sourcecode:: python + + add_attribute('Blog', 'category') + +5. you can restart your instance, modify a blog entity and check that the new attribute +``category`` has been added. + +Of course, you may also want to add relations, entity types, ... See :ref:`migration` +for a list of all available migration commands. + diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,30 @@ +.. -*- coding: utf-8 -*- + +.. _Tutorial: + +.. _tuto_blog: + +Building a simple blog +====================== + +*CubicWeb* is a semantic web application framework that favors reuse and +object-oriented design. + +A `cube` is a component that includes a model defining the data types and a set of +views to display the data. A cube can be built by assembling other cubes. + +An `instance` is a specific installation of a cube and includes configuration +files. + + +This tutorial will show how to create a `cube` and how to use it as an +application to run an `instance`. + +.. toctree:: + :maxdepth: 2 + + blog-in-five-minutes + create-cube + components + maintemplate + conclusion diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/base/maintemplate.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/base/maintemplate.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,131 @@ +.. -*- coding: utf-8 -*- + +Templates +--------- + +Look at ``cubicweb/web/views/basetemplates.py`` and you will +find the base templates used to generate HTML for your application. + +A page is composed as indicated on the schema below: + +.. image:: ../../images/lax-book.06-main-template-layout.en.png + +In this section we will demonstrate a change in one of the main +interesting template from the three you will look for, +that is to say, the HTMLPageHeader, the HTMLPageFooter +and the TheMainTemplate. + + +Customize a template +~~~~~~~~~~~~~~~~~~~~ + +Based on the diagram below, each template can be overriden +by your customized template. To do so, we recommand you create +a Python module ``blog.views.templates`` to keep it organized. +In this module you will have to import the parent class you are +interested as follows: :: + + from cubicweb.web.views.basetemplates import HTMLPageHeader, \ + HTMLPageFooter, TheMainTemplate + +and then create your sub-class:: + + class MyBlogHTMLPageHeader(HTMLPageHeader): + ... + +Customize header +````````````````` + +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 user interface. + +Let's say we do not want anymore the login menu to be in the header + +First, to remove the login menu, we just need to comment out the display of the +login graphic component such as follows: + +.. sourcecode:: python + + class MyBlogHTMLPageHeader(HTMLPageHeader): + + def main_header(self, view): + """build the top menu with authentification info and the rql box""" + self.w(u'\n') + self.w(u'\n') + # appliname and breadcrumbs + self.w(u'') + # logged user and help + #self.w(u'') + # lastcolumn + self.w(u'\n') + self.w(u'\n') + self.template('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden', + title=False, message=False) + + + +.. image:: ../../images/lax-book.06-header-no-login.en.png + +Customize footer +```````````````` + +If you want to change the footer for example, look +for HTMLPageFooter and override it in your views file as in: + +.. sourcecode:: python + + from cubicweb.web.views.basetemplates import HTMLPageFooter + + class MyHTMLPageFooter(HTMLPageFooter): + + def call(self, **kwargs): + self.w(u'') + +Updating a view does not require any restart of the server. By reloading +the page you can see your new page footer. + + +TheMainTemplate +``````````````` + +.. _TheMainTemplate: + +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. + +TheMainTemplate is responsible for the general layout of the entire application. +It defines the template of ``__regid__ = main`` that is used by the application. Is +also defined in ``cubicweb/web/views/basetemplates.py`` another template that can +be used based on TheMainTemplate called SimpleMainTemplate which does not have +a top section. + +.. image:: ../../images/lax-book.06-simple-main-template.en.png + +XXX +[WRITE ME] + +* customize MainTemplate and show that everything in the user + interface can be changed + diff -r f7d2df59231a -r 16461f675734 doc/book/en/tutorials/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/tutorials/index.rst Thu Apr 15 12:48:40 2010 +0200 @@ -0,0 +1,19 @@ +.. _Tutorials: + +--------- +Tutorials +--------- + +We present two tutorials of different levels. The blog building +tutorial introduces one smoothly to the basic concepts. + +Then there is a photo gallery construction tutorial which highlights +more advanced concepts such as unit tests, security settings, +migration scripts. + +.. toctree:: + :maxdepth: 1 + :numbered: + + base/index + advanced/index diff -r f7d2df59231a -r 16461f675734 etwist/server.py --- a/etwist/server.py Thu Apr 15 12:47:02 2010 +0200 +++ b/etwist/server.py Thu Apr 15 12:48:40 2010 +0200 @@ -194,7 +194,7 @@ """Render a page from the root resource""" # reload modified files in debug mode if self.debugmode: - self.appli.vreg.register_objects(self.config.vregistry_path()) + self.appli.vreg.reload_if_needed() if self.config['profile']: # default profiler don't trace threads return self.render_request(request) else: diff -r f7d2df59231a -r 16461f675734 vregistry.py --- a/vregistry.py Thu Apr 15 12:47:02 2010 +0200 +++ b/vregistry.py Thu Apr 15 12:48:40 2010 +0200 @@ -31,8 +31,7 @@ from logilab.common.logging_ext import set_log_methods from cubicweb import CW_SOFTWARE_ROOT -from cubicweb import (RegistryNotFound, ObjectNotFound, NoSelectableObject, - RegistryOutOfDate) +from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject from cubicweb.appobject import AppObject def _toload_info(path, extrapath, _toload=None): @@ -246,8 +245,18 @@ def __init__(self, config): super(VRegistry, self).__init__() self.config = config + # need to clean sys.path this to avoid import confusion pb (i.e. having + # the same module loaded as 'cubicweb.web.views' subpackage and as + # views' or 'web.views' subpackage. This is mainly for testing purpose, + # we should'nt need this in production environment + for webdir in (join(dirname(realpath(__file__)), 'web'), + join(dirname(__file__), 'web')): + if webdir in sys.path: + sys.path.remove(webdir) + if CW_SOFTWARE_ROOT in sys.path: + sys.path.remove(CW_SOFTWARE_ROOT) - def reset(self, path=None, force_reload=None): + def reset(self): # don't use self.clear, we want to keep existing subdictionaries for subdict in self.itervalues(): subdict.clear() @@ -392,60 +401,61 @@ self._loadedmods = {} return filemods - def register_objects(self, path, force_reload, extrapath=None): - # need to clean sys.path this to avoid import confusion pb (i.e. - # having the same module loaded as 'cubicweb.web.views' subpackage and - # as views' or 'web.views' subpackage - # this is mainly for testing purpose, we should'nt need this in - # production environment - for webdir in (join(dirname(realpath(__file__)), 'web'), - join(dirname(__file__), 'web')): - if webdir in sys.path: - sys.path.remove(webdir) - if CW_SOFTWARE_ROOT in sys.path: - sys.path.remove(CW_SOFTWARE_ROOT) + def register_objects(self, path, force_reload=False, extrapath=None): # load views from each directory in the instance's path filemods = self.init_registration(path, extrapath) - change = False for filepath, modname in filemods: - if self.load_file(filepath, modname, force_reload): - change = True - if change: - self.initialization_completed() - return change + self.load_file(filepath, modname, force_reload) + self.initialization_completed() def initialization_completed(self): for regname, reg in self.iteritems(): reg.initialization_completed() + def _mdate(self, filepath): + try: + return stat(filepath)[-2] + except OSError: + # this typically happens on emacs backup files (.#foo.py) + self.warning('Unable to load %s. It is likely to be a backup file', + filepath) + return None + + def is_reload_needed(self, path): + """return True if something module changed and the registry should be + reloaded + """ + lastmodifs = self._lastmodifs + for fileordir in path: + if isdir(fileordir) and exists(join(fileordir, '__init__.py')): + if self.is_reload_needed([join(fileordir, fname) + for fname in listdir(fileordir)]): + return True + elif fileordir[-3:] == '.py': + mdate = self._mdate(fileordir) + if mdate is None: + continue # backup file, see _mdate implementation + if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate: + self.info('File %s changed since last visit', fileordir) + return True + return False + def load_file(self, filepath, modname, force_reload=False): """load app objects from a python file""" from logilab.common.modutils import load_module_from_name if modname in self._loadedmods: return self._loadedmods[modname] = {} - try: - modified_on = stat(filepath)[-2] - except OSError: - # this typically happens on emacs backup files (.#foo.py) - self.warning('Unable to load %s. It is likely to be a backup file', - filepath) - return False - if filepath in self._lastmodifs: - # only load file if it was modified - if modified_on <= self._lastmodifs[filepath]: - return - # if it was modified, raise RegistryOutOfDate to reload everything - self.info('File %s changed since last visit', filepath) - raise RegistryOutOfDate() + mdate = self._mdate(filepath) + if mdate is None: + return # backup file, see _mdate implementation # set update time before module loading, else we get some reloading # weirdness in case of syntax error or other error while importing the # module - self._lastmodifs[filepath] = modified_on + self._lastmodifs[filepath] = mdate # load the module module = load_module_from_name(modname, use_sys=not force_reload) self.load_module(module) - return True def load_module(self, module): self.info('loading %s', module) diff -r f7d2df59231a -r 16461f675734 web/uicfg.py --- a/web/uicfg.py Thu Apr 15 12:47:02 2010 +0200 +++ b/web/uicfg.py Thu Apr 15 12:48:40 2010 +0200 @@ -8,107 +8,6 @@ To configure the interface generation, we use ``RelationTag`` objects. -Primary view configuration -`````````````````````````` - -XXX section moved to the doc, is maintained there - -If you want to customize the primary view of an entity, overriding the primary -view class may not be necessary. For simple adjustments (attributes or relations -display locations and styles), a much simpler way is to use uicfg. - -Attributes/relations display location -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the primary view, there are 3 sections where attributes and relations can be -displayed (represented in pink in the image below): - -* attributes -* relations -* sideboxes - -.. image:: ../../images/primaryview_template.png - - -**Attributes** can only be displayed in the attributes section (default - behavior). They can also be hidden. - -For instance, to hide the ``title`` attribute of the ``Blog`` entity: - -.. sourcecode:: python - - from cubicweb.web import uicfg - uicfg.primaryview_section.tag_attribute(('Blog', 'title'), 'hidden') - - -**Relations** can be either displayed in one of the three sections or hidden. - -For relations, there are two methods: - -* ``tag_object_of`` for modifying the primary view of the object -* ``tag_subject_of`` for modifying the primary view of the subject - -These two methods take two arguments: - -* a triplet ``(subject, relation_name, object)``, where subject or object can be replaced with ``'*'`` -* the section name or ``hidden`` - -.. sourcecode:: python - - # hide every relation ``entry_of`` in the ``Blog`` primary view - uicfg.primaryview_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden') - - # display ``entry_of`` relations in the ``relations`` section in the ``BlogEntry`` primary view - uicfg.primaryview_section.tag_subject_of(('BlogEntry', 'entry_of', '*'), - 'relations') - - -Display content -^^^^^^^^^^^^^^^ - -You can use ``primaryview_display_ctrl`` to customize the display of attributes -or relations. Values of ``primaryview_display_ctrl`` are dictionaries. - - -Common keys for attributes and relations are: -* ``vid``: specifies the regid of the view for displaying the attribute or the relation. - - If ``vid`` is not specified, the default value depends on the section: - * ``attributes`` section: 'reledit' view - * ``relations`` section: 'autolimited' view - * ``sideboxes`` section: 'sidebox' view - -* ``order``: int used to control order within a section. When not specified, - automatically set according to order in which tags are added. - - -Keys for relations only: - -* ``label``: label for the relations section or side box - -* ``showlabel``: boolean telling whether the label is displayed - -* ``limit``: boolean telling if the results should be limited. If so, a link to all results is displayed - -* ``filter``: callback taking the related result set as argument and returning it filtered - -.. sourcecode:: python - - # in ``CWUser`` primary view, display ``created_by`` relations in relations section - uicfg.primaryview_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations') - - # displays this relation as a list, sets the label, limits the number of results and filters on comments - uicfg.primaryview_display_ctrl.tag_object_of( - ('*', 'created_by', 'CWUser'), - {'vid': 'list', 'label': _('latest comment(s):'), 'limit': True, - 'filter': lambda rset: rset.filtered_rset(lambda x: x.e_schema == 'Comment')}) - -.. Warning:: with the ``primaryview_display_ctrl`` rtag, the subject or the - object of the relation is ignored for respectively ``tag_object_of`` or - ``tag_subject_of``. To avoid warnings during execution, they should be set to - ``'*'``. - - Index view configuration ```````````````````````` :indexview_etype_section: