backport stable branch
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 31 Jul 2009 14:25:30 +0200
changeset 2589 92f2bc945261
parent 2586 879912fe94e1 (diff)
parent 2588 3a590ff82e99 (current diff)
child 2590 45d5f2d73561
backport stable branch
server/__init__.py
server/migractions.py
server/session.py
server/sources/native.py
vregistry.py
--- a/README	Fri Jul 31 14:18:53 2009 +0200
+++ b/README	Fri Jul 31 14:25:30 2009 +0200
@@ -1,18 +1,27 @@
-CubicWeb semantic web framework 
+CubicWeb semantic web framework
 ===============================
- 
+
 Install
 -------
-From the source distribution, extract the tarball and run ::
-  
-    python setup.py install
-  
-For deb and rpm packages, use the tools recommended by your distribution.
+
+More details at http://www.cubicweb.org/doc/en/admin/setup
+
+Getting started
+---------------
+
+Execute:
 
-  
+ apt-get install cubicweb cubicweb-dev cubicweb-blog
+ cubicweb-ctl create blog myblog
+ cubicweb-ctl start -D myblog
+ sensible-browser http://localhost:8080/
+
+Details at http://www.cubicweb.org/doc/en/intro/tutorial/blog-in-five-minutes
+
 Documentation
 -------------
-Look in the doc/ subdirectory.
+
+Look in the doc/ subdirectory or read http://www.cubicweb.org/doc/en/
 
 
 
--- a/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -109,11 +109,16 @@
 
     # url generation methods ##################################################
 
-    def build_url(self, method, base_url=None, **kwargs):
+    def build_url(self, *args, **kwargs):
         """return an absolute URL using params dictionary key/values as URL
         parameters. Values are automatically URL quoted, and the
         publishing method to use may be specified or will be guessed.
         """
+        # use *args since we don't want first argument to be "anonymous" to
+        # avoid potential clash with kwargs
+        assert len(args) == 1, 'only 0 or 1 non-named-argument expected'
+        method = args[0]
+        base_url = kwargs.pop('base_url', None)
         if base_url is None:
             base_url = self.base_url()
         if '_restpath' in kwargs:
@@ -193,7 +198,7 @@
     # abstract methods to override according to the web front-end #############
 
     def base_url(self):
-        """return the root url of the application"""
+        """return the root url of the instance"""
         raise NotImplementedError
 
     def decorate_rset(self, rset):
--- a/__pkginfo__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/__pkginfo__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -7,7 +7,7 @@
 distname = "cubicweb"
 modname = "cubicweb"
 
-numversion = (3, 3, 4)
+numversion = (3, 4, 0)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL v2'
--- a/appobject.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/appobject.py	Fri Jul 31 14:25:30 2009 +0200
@@ -40,11 +40,11 @@
 
     At registration time, the following attributes are set on the class:
     :vreg:
-      the application's registry
+      the instance's registry
     :schema:
-      the application's schema
+      the instance's schema
     :config:
-      the application's configuration
+      the instance's configuration
 
     At instantiation time, the following attributes are set on the instance:
     :req:
@@ -151,7 +151,8 @@
         # try to get page boundaries from the navigation component
         # XXX we should probably not have a ref to this component here (eg in
         #     cubicweb.common)
-        nav = self.vreg.select_component('navigation', self.req, self.rset)
+        nav = self.vreg.select_object('components', 'navigation', self.req,
+                                      rset=self.rset)
         if nav:
             start, stop = nav.page_boundaries()
             rql = self._limit_offset_rql(stop - start, start)
@@ -189,7 +190,8 @@
 
     def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
         """shortcut to self.vreg.view method avoiding to pass self.req"""
-        return self.vreg.view(__vid, self.req, rset, __fallback_vid, **kwargs)
+        return self.vreg.render(__vid, self.req, __fallback_vid, rset=rset,
+                                **kwargs)
 
     def initialize_varmaker(self):
         varmaker = self.req.get_page_data('rql_varmaker')
@@ -202,11 +204,18 @@
 
     controller = 'view'
 
-    def build_url(self, method=None, **kwargs):
+    def build_url(self, *args, **kwargs):
         """return an absolute URL using params dictionary key/values as URL
         parameters. Values are automatically URL quoted, and the
         publishing method to use may be specified or will be guessed.
         """
+        # use *args since we don't want first argument to be "anonymous" to
+        # avoid potential clash with kwargs
+        if args:
+            assert len(args) == 1, 'only 0 or 1 non-named-argument expected'
+            method = args[0]
+        else:
+            method = None
         # XXX I (adim) think that if method is passed explicitly, we should
         #     not try to process it and directly call req.build_url()
         if method is None:
@@ -267,7 +276,7 @@
         return output.getvalue()
 
     def format_date(self, date, date_format=None, time=False):
-        """return a string for a date time according to application's
+        """return a string for a date time according to instance's
         configuration
         """
         if date:
@@ -280,7 +289,7 @@
         return u''
 
     def format_time(self, time):
-        """return a string for a time according to application's
+        """return a string for a time according to instance's
         configuration
         """
         if time:
@@ -288,7 +297,7 @@
         return u''
 
     def format_float(self, num):
-        """return a string for floating point number according to application's
+        """return a string for floating point number according to instance's
         configuration
         """
         if num:
--- a/common/i18n.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/i18n.py	Fri Jul 31 14:25:30 2009 +0200
@@ -73,7 +73,7 @@
         pofiles = [pof for pof in pofiles if exists(pof)]
         mergedpo = join(destdir, '%s_merged.po' % lang)
         try:
-            # merge application messages' catalog with the stdlib's one
+            # merge instance/cubes messages catalogs with the stdlib's one
             execute('msgcat --use-first --sort-output --strict %s > %s'
                     % (' '.join(pofiles), mergedpo))
             # make sure the .mo file is writeable and compile with *msgfmt*
--- a/common/mail.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/mail.py	Fri Jul 31 14:25:30 2009 +0200
@@ -18,7 +18,7 @@
 
 def addrheader(uaddr, uname=None):
     # even if an email address should be ascii, encode it using utf8 since
-    # application tests may generate non ascii email address
+    # automatic tests may generate non ascii email address
     addr = uaddr.encode('UTF-8')
     if uname:
         return '%s <%s>' % (header(uname).encode(), addr)
--- a/common/migration.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/migration.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,5 +1,4 @@
-"""utility to ease migration of application version to newly installed
-version
+"""utilities for instances migration
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -11,7 +10,7 @@
 import sys
 import os
 import logging
-from tempfile import mktemp
+import tempfile
 from os.path import exists, join, basename, splitext
 
 from logilab.common.decorators import cached
@@ -70,7 +69,7 @@
     ability to show the script's content
     """
     while True:
-        confirm = raw_input('** execute %r (Y/n/s[how]) ?' % scriptpath)
+        confirm = raw_input('Execute %r (Y/n/s[how]) ?' % scriptpath)
         confirm = confirm.strip().lower()
         if confirm in ('n', 'no'):
             return False
@@ -153,11 +152,15 @@
                 migrdir = self.config.cube_migration_scripts_dir(cube)
             scripts = filter_scripts(self.config, migrdir, fromversion, toversion)
             if scripts:
+                prevversion = None
                 for version, script in scripts:
+                    # take care to X.Y.Z_Any.py / X.Y.Z_common.py: we've to call
+                    # cube_upgraded once all script of X.Y.Z have been executed
+                    if prevversion is not None and version != prevversion:
+                        self.cube_upgraded(cube, version)
+                    prevversion = version
                     self.process_script(script)
-                    self.cube_upgraded(cube, version)
-                if version != toversion:
-                    self.cube_upgraded(cube, toversion)
+                self.cube_upgraded(cube, toversion)
             else:
                 self.cube_upgraded(cube, toversion)
 
@@ -169,7 +172,7 @@
 
     def interact(self, args, kwargs, meth):
         """execute the given method according to user's confirmation"""
-        msg = 'execute command: %s(%s) ?' % (
+        msg = 'Execute command: %s(%s) ?' % (
             meth.__name__[4:],
             ', '.join([repr(arg) for arg in args] +
                       ['%s=%r' % (n,v) for n,v in kwargs.items()]))
@@ -337,7 +340,7 @@
         configfile = self.config.main_config_file()
         if self._option_changes:
             read_old_config(self.config, self._option_changes, configfile)
-        newconfig = mktemp()
+        _, newconfig = tempfile.mkstemp()
         for optdescr in self._option_changes:
             if optdescr[0] == 'added':
                 optdict = self.config.get_option_def(optdescr[1])
--- a/common/mixins.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/mixins.py	Fri Jul 31 14:25:30 2009 +0200
@@ -244,7 +244,7 @@
 
     @obsolete('use EntityFieldsForm.subject_in_state_vocabulary')
     def subject_in_state_vocabulary(self, rschema, limit=None):
-        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        form = self.vreg.select('forms', 'edition', self.req, entity=self)
         return form.subject_in_state_vocabulary(rschema, limit)
 
 
--- a/common/mttransforms.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/mttransforms.py	Fri Jul 31 14:25:30 2009 +0200
@@ -46,8 +46,8 @@
     from cubicweb.ext.tal import compile_template
 except ImportError:
     HAS_TAL = False
-    from cubicweb.schema import FormatConstraint
-    FormatConstraint.need_perm_formats.remove('text/cubicweb-page-template')
+    from cubicweb import schema
+    schema.NEED_PERM_FORMATS.remove('text/cubicweb-page-template')
 
 else:
     HAS_TAL = True
--- a/common/tags.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/tags.py	Fri Jul 31 14:25:30 2009 +0200
@@ -7,7 +7,7 @@
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.uilib import simple_sgml_tag
+from cubicweb.common.uilib import simple_sgml_tag, sgml_attributes
 
 class tag(object):
     def __init__(self, name, escapecontent=True):
@@ -38,8 +38,7 @@
     if id:
         attrs['id'] = id
     attrs['name'] = name
-    html = [u'<select %s>' % ' '.join('%s="%s"' % kv
-                                      for kv in sorted(attrs.items()))]
+    html = [u'<select %s>' % sgml_attributes(attrs)]
     html += options
     html.append(u'</select>')
     return u'\n'.join(html)
--- a/common/uilib.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/common/uilib.py	Fri Jul 31 14:25:30 2009 +0200
@@ -212,10 +212,15 @@
 HTML4_EMPTY_TAGS = frozenset(('base', 'meta', 'link', 'hr', 'br', 'param',
                               'img', 'area', 'input', 'col'))
 
+def sgml_attributes(attrs):
+    return u' '.join(u'%s="%s"' % (attr, xml_escape(unicode(value)))
+                     for attr, value in sorted(attrs.items())
+                     if value is not None)
+
 def simple_sgml_tag(tag, content=None, escapecontent=True, **attrs):
     """generation of a simple sgml tag (eg without children tags) easier
 
-    content and attributes will be escaped
+    content and attri butes will be escaped
     """
     value = u'<%s' % tag
     if attrs:
@@ -223,9 +228,7 @@
             attrs['class'] = attrs.pop('klass')
         except KeyError:
             pass
-        value += u' ' + u' '.join(u'%s="%s"' % (attr, xml_escape(unicode(value)))
-                                  for attr, value in sorted(attrs.items())
-                                  if value is not None)
+        value += u' ' + sgml_attributes(attrs)
     if content:
         if escapecontent:
             content = xml_escape(unicode(content))
--- a/cwconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/cwconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -22,6 +22,7 @@
 from os.path import exists, join, expanduser, abspath, normpath, basename, isdir
 
 from logilab.common.decorators import cached
+from logilab.common.deprecation import deprecated_function
 from logilab.common.logging_ext import set_log_methods, init_log
 from logilab.common.configuration import (Configuration, Method,
                                           ConfigurationMixIn, merge_options)
@@ -149,7 +150,7 @@
         CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ
         # create __init__ file
         file(join(CUBES_DIR, '__init__.py'), 'w').close()
-    elif exists(join(CW_SOFTWARE_ROOT, '.hg')):
+    elif exists(join(CW_SOFTWARE_ROOT, '.hg')) or os.environ.get('CW_MODE') == 'user':
         mode = 'dev'
         CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
     else:
@@ -221,7 +222,7 @@
           'group': 'appobjects', 'inputlevel': 2,
           }),
         )
-    # static and class methods used to get application independant resources ##
+    # static and class methods used to get instance independant resources ##
 
     @staticmethod
     def cubicweb_version():
@@ -247,7 +248,7 @@
 
     @classmethod
     def i18n_lib_dir(cls):
-        """return application's i18n directory"""
+        """return instance's i18n directory"""
         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
             return join(CW_SOFTWARE_ROOT, 'i18n')
         return join(cls.shared_dir(), 'i18n')
@@ -424,7 +425,7 @@
     @classmethod
     def build_vregistry_path(cls, templpath, evobjpath=None, tvobjpath=None):
         """given a list of directories, return a list of sub files and
-        directories that should be loaded by the application objects registry.
+        directories that should be loaded by the instance objects registry.
 
         :param evobjpath:
           optional list of sub-directories (or files without the .py ext) of
@@ -539,8 +540,10 @@
 
     # for some commands (creation...) we don't want to initialize gettext
     set_language = True
-    # set this to true to avoid false error message while creating an application
+    # set this to true to avoid false error message while creating an instance
     creating = False
+    # set this to true to allow somethings which would'nt be possible
+    repairing = False
 
     options = CubicWebNoAppConfiguration.options + (
         ('log-file',
@@ -564,7 +567,7 @@
           }),
         ('sender-name',
          {'type' : 'string',
-          'default': Method('default_application_id'),
+          'default': Method('default_instance_id'),
           'help': 'name used as HELO name for outgoing emails from the \
 repository.',
           'group': 'email', 'inputlevel': 2,
@@ -581,49 +584,49 @@
     @classmethod
     def runtime_dir(cls):
         """run time directory for pid file..."""
-        return env_path('CW_RUNTIME', cls.RUNTIME_DIR, 'run time')
+        return env_path('CW_RUNTIME_DIR', cls.RUNTIME_DIR, 'run time')
 
     @classmethod
     def registry_dir(cls):
         """return the control directory"""
-        return env_path('CW_REGISTRY', cls.REGISTRY_DIR, 'registry')
+        return env_path('CW_INSTANCES_DIR', cls.REGISTRY_DIR, 'registry')
 
     @classmethod
     def instance_data_dir(cls):
         """return the instance data directory"""
-        return env_path('CW_INSTANCE_DATA',
+        return env_path('CW_INSTANCES_DATA_DIR',
                         cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
                         'additional data')
 
     @classmethod
     def migration_scripts_dir(cls):
         """cubicweb migration scripts directory"""
-        return env_path('CW_MIGRATION', cls.MIGRATION_DIR, 'migration')
+        return env_path('CW_MIGRATION_DIR', cls.MIGRATION_DIR, 'migration')
 
     @classmethod
     def config_for(cls, appid, config=None):
-        """return a configuration instance for the given application identifier
+        """return a configuration instance for the given instance identifier
         """
-        config = config or guess_configuration(cls.application_home(appid))
+        config = config or guess_configuration(cls.instance_home(appid))
         configcls = configuration_cls(config)
         return configcls(appid)
 
     @classmethod
     def possible_configurations(cls, appid):
         """return the name of possible configurations for the given
-        application id
+        instance id
         """
-        home = cls.application_home(appid)
+        home = cls.instance_home(appid)
         return possible_configurations(home)
 
     @classmethod
-    def application_home(cls, appid):
-        """return the home directory of the application with the given
-        application id
+    def instance_home(cls, appid):
+        """return the home directory of the instance with the given
+        instance id
         """
         home = join(cls.registry_dir(), appid)
         if not exists(home):
-            raise ConfigurationError('no such application %s (check it exists with "cubicweb-ctl list")' % appid)
+            raise ConfigurationError('no such instance %s (check it exists with "cubicweb-ctl list")' % appid)
         return home
 
     MODES = ('common', 'repository', 'Any', 'web')
@@ -637,14 +640,14 @@
 
     # default configuration methods ###########################################
 
-    def default_application_id(self):
-        """return the application identifier, useful for option which need this
+    def default_instance_id(self):
+        """return the instance identifier, useful for option which need this
         as default value
         """
         return self.appid
 
     def default_log_file(self):
-        """return default path to the log file of the application'server"""
+        """return default path to the log file of the instance'server"""
         if self.mode == 'dev':
             basepath = '/tmp/%s-%s' % (basename(self.appid), self.name)
             path = basepath + '.log'
@@ -660,10 +663,10 @@
         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
 
     def default_pid_file(self):
-        """return default path to the pid file of the application'server"""
+        """return default path to the pid file of the instance'server"""
         return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name))
 
-    # instance methods used to get application specific resources #############
+    # instance methods used to get instance specific resources #############
 
     def __init__(self, appid):
         self.appid = appid
@@ -722,7 +725,7 @@
         self._cubes = self.reorder_cubes(list(self._cubes) + cubes)
 
     def main_config_file(self):
-        """return application's control configuration file"""
+        """return instance's control configuration file"""
         return join(self.apphome, '%s.conf' % self.name)
 
     def save(self):
@@ -739,7 +742,7 @@
         return md5.new(';'.join(infos)).hexdigest()
 
     def load_site_cubicweb(self):
-        """load (web?) application's specific site_cubicweb file"""
+        """load instance's specific site_cubicweb file"""
         for path in reversed([self.apphome] + self.cubes_path()):
             sitefile = join(path, 'site_cubicweb.py')
             if exists(sitefile) and not sitefile in self._site_loaded:
@@ -762,7 +765,7 @@
             self.load_defaults()
 
     def load_configuration(self):
-        """load application's configuration files"""
+        """load instance's configuration files"""
         super(CubicWebConfiguration, self).load_configuration()
         if self.apphome and self.set_language:
             # init gettext
@@ -781,7 +784,7 @@
             logging.fileConfig(logconfig)
 
     def available_languages(self, *args):
-        """return available translation for an application, by looking for
+        """return available translation for an instance, by looking for
         compiled catalog
 
         take *args to be usable as a vocabulary method
@@ -861,6 +864,6 @@
 
 set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration'))
 
-# alias to get a configuration instance from an application id
-application_configuration = CubicWebConfiguration.config_for
-
+# alias to get a configuration instance from an instance id
+instance_configuration = CubicWebConfiguration.config_for
+application_configuration = deprecated_function(instance_configuration, 'use instance_configuration')
--- a/cwctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/cwctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,6 +1,6 @@
 """%%prog %s [options] %s
 
-CubicWeb main applications controller.
+CubicWeb main instances controller.
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 %s"""
 
@@ -45,11 +45,11 @@
     return modes
 
 
-class ApplicationCommand(Command):
-    """base class for command taking 0 to n application id as arguments
-    (0 meaning all registered applications)
+class InstanceCommand(Command):
+    """base class for command taking 0 to n instance id as arguments
+    (0 meaning all registered instances)
     """
-    arguments = '[<application>...]'
+    arguments = '[<instance>...]'
     options = (
         ("force",
          {'short': 'f', 'action' : 'store_true',
@@ -84,7 +84,7 @@
         return allinstances
 
     def run(self, args):
-        """run the <command>_method on each argument (a list of application
+        """run the <command>_method on each argument (a list of instance
         identifiers)
         """
         if not args:
@@ -102,29 +102,29 @@
         for appid in args:
             if askconfirm:
                 print '*'*72
-                if not confirm('%s application %r ?' % (self.name, appid)):
+                if not confirm('%s instance %r ?' % (self.name, appid)):
                     continue
             self.run_arg(appid)
 
     def run_arg(self, appid):
-        cmdmeth = getattr(self, '%s_application' % self.name)
+        cmdmeth = getattr(self, '%s_instance' % self.name)
         try:
             cmdmeth(appid)
         except (KeyboardInterrupt, SystemExit):
             print >> sys.stderr, '%s aborted' % self.name
             sys.exit(2) # specific error code
         except (ExecutionError, ConfigurationError), ex:
-            print >> sys.stderr, 'application %s not %s: %s' % (
+            print >> sys.stderr, 'instance %s not %s: %s' % (
                 appid, self.actionverb, ex)
         except Exception, ex:
             import traceback
             traceback.print_exc()
-            print >> sys.stderr, 'application %s not %s: %s' % (
+            print >> sys.stderr, 'instance %s not %s: %s' % (
                 appid, self.actionverb, ex)
 
 
-class ApplicationCommandFork(ApplicationCommand):
-    """Same as `ApplicationCommand`, but command is forked in a new environment
+class InstanceCommandFork(InstanceCommand):
+    """Same as `InstanceCommand`, but command is forked in a new environment
     for each argument
     """
 
@@ -136,7 +136,7 @@
         for appid in args:
             if askconfirm:
                 print '*'*72
-                if not confirm('%s application %r ?' % (self.name, appid)):
+                if not confirm('%s instance %r ?' % (self.name, appid)):
                     continue
             if forkcmd:
                 status = system('%s %s' % (forkcmd, appid))
@@ -148,10 +148,9 @@
 # base commands ###############################################################
 
 class ListCommand(Command):
-    """List configurations, componants and applications.
+    """List configurations, cubes and instances.
 
-    list available configurations, installed web and server componants, and
-    registered applications
+    list available configurations, installed cubes, and registered instances
     """
     name = 'list'
     options = (
@@ -206,30 +205,30 @@
         try:
             regdir = cwcfg.registry_dir()
         except ConfigurationError, ex:
-            print 'No application available:', ex
+            print 'No instance available:', ex
             print
             return
         instances = list_instances(regdir)
         if instances:
-            print 'Available applications (%s):' % regdir
+            print 'Available instances (%s):' % regdir
             for appid in instances:
                 modes = cwcfg.possible_configurations(appid)
                 if not modes:
-                    print '* %s (BROKEN application, no configuration found)' % appid
+                    print '* %s (BROKEN instance, no configuration found)' % appid
                     continue
                 print '* %s (%s)' % (appid, ', '.join(modes))
                 try:
                     config = cwcfg.config_for(appid, modes[0])
                 except Exception, exc:
-                    print '    (BROKEN application, %s)' % exc
+                    print '    (BROKEN instance, %s)' % exc
                     continue
         else:
-            print 'No application available in %s' % regdir
+            print 'No instance available in %s' % regdir
         print
 
 
-class CreateApplicationCommand(Command):
-    """Create an application from a cube. This is an unified
+class CreateInstanceCommand(Command):
+    """Create an instance from a cube. This is an unified
     command which can handle web / server / all-in-one installation
     according to available parts of the software library and of the
     desired cube.
@@ -238,11 +237,11 @@
       the name of cube to use (list available cube names using
       the "list" command). You can use several cubes by separating
       them using comma (e.g. 'jpl,eemail')
-    <application>
-      an identifier for the application to create
+    <instance>
+      an identifier for the instance to create
     """
     name = 'create'
-    arguments = '<cube> <application>'
+    arguments = '<cube> <instance>'
     options = (
         ("config-level",
          {'short': 'l', 'type' : 'int', 'metavar': '<level>',
@@ -255,7 +254,7 @@
          {'short': 'c', 'type' : 'choice', 'metavar': '<install type>',
           'choices': ('all-in-one', 'repository', 'twisted'),
           'default': 'all-in-one',
-          'help': 'installation type, telling which part of an application \
+          'help': 'installation type, telling which part of an instance \
 should be installed. You can list available configurations using the "list" \
 command. Default to "all-in-one", e.g. an installation embedding both the RQL \
 repository and the web server.',
@@ -285,13 +284,13 @@
             print '\navailable cubes:',
             print ', '.join(cwcfg.available_cubes())
             return
-        # create the registry directory for this application
-        print '\n'+underline_title('Creating the application %s' % appid)
+        # create the registry directory for this instance
+        print '\n'+underline_title('Creating the instance %s' % appid)
         create_dir(config.apphome)
         # load site_cubicweb from the cubes dir (if any)
         config.load_site_cubicweb()
         # cubicweb-ctl configuration
-        print '\n'+underline_title('Configuring the application (%s.conf)' % configname)
+        print '\n'+underline_title('Configuring the instance (%s.conf)' % configname)
         config.input_config('main', self.config.config_level)
         # configuration'specific stuff
         print
@@ -311,9 +310,10 @@
                            'continue anyway ?'):
                 print 'creation not completed'
                 return
-        # create the additional data directory for this application
+        # create the additional data directory for this instance
         if config.appdatahome != config.apphome: # true in dev mode
             create_dir(config.appdatahome)
+        create_dir(join(config.appdatahome, 'backup'))
         if config['uid']:
             from logilab.common.shellutils import chown
             # this directory should be owned by the uid of the server process
@@ -323,18 +323,18 @@
         helper.postcreate()
 
 
-class DeleteApplicationCommand(Command):
-    """Delete an application. Will remove application's files and
+class DeleteInstanceCommand(Command):
+    """Delete an instance. Will remove instance's files and
     unregister it.
     """
     name = 'delete'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = ()
 
     def run(self, args):
         """run the command with its specific arguments"""
-        appid = pop_arg(args, msg="No application specified !")
+        appid = pop_arg(args, msg="No instance specified !")
         configs = [cwcfg.config_for(appid, configname)
                    for configname in cwcfg.possible_configurations(appid)]
         if not configs:
@@ -353,16 +353,16 @@
             if ex.errno != errno.ENOENT:
                 raise
         confignames = ', '.join([config.name for config in configs])
-        print 'application %s (%s) deleted' % (appid, confignames)
+        print '-> instance %s (%s) deleted.' % (appid, confignames)
 
 
-# application commands ########################################################
+# instance commands ########################################################
 
-class StartApplicationCommand(ApplicationCommand):
-    """Start the given applications. If no application is given, start them all.
+class StartInstanceCommand(InstanceCommand):
+    """Start the given instances. If no instance is given, start them all.
 
-    <application>...
-      identifiers of the applications to start. If no application is
+    <instance>...
+      identifiers of the instances to start. If no instance is
       given, start them all.
     """
     name = 'start'
@@ -374,7 +374,7 @@
         ("force",
          {'short': 'f', 'action' : 'store_true',
           'default': False,
-          'help': 'start the application even if it seems to be already \
+          'help': 'start the instance even if it seems to be already \
 running.'}),
         ('profile',
          {'short': 'P', 'type' : 'string', 'metavar': '<stat file>',
@@ -383,8 +383,8 @@
           }),
         )
 
-    def start_application(self, appid):
-        """start the application's server"""
+    def start_instance(self, appid):
+        """start the instance's server"""
         # use get() since start may be used from other commands (eg upgrade)
         # without all options defined
         debug = self.get('debug')
@@ -403,31 +403,31 @@
             print "starting server with command :"
             print command
         if system(command):
-            print 'an error occured while starting the application, not started'
+            print 'an error occured while starting the instance, not started'
             print
             return False
         if not debug:
-            print 'application %s started' % appid
+            print '-> instance %s started.' % appid
         return True
 
 
-class StopApplicationCommand(ApplicationCommand):
-    """Stop the given applications.
+class StopInstanceCommand(InstanceCommand):
+    """Stop the given instances.
 
-    <application>...
-      identifiers of the applications to stop. If no application is
+    <instance>...
+      identifiers of the instances to stop. If no instance is
       given, stop them all.
     """
     name = 'stop'
     actionverb = 'stopped'
 
     def ordered_instances(self):
-        instances = super(StopApplicationCommand, self).ordered_instances()
+        instances = super(StopInstanceCommand, self).ordered_instances()
         instances.reverse()
         return instances
 
-    def stop_application(self, appid):
-        """stop the application's server"""
+    def stop_instance(self, appid):
+        """stop the instance's server"""
         config = cwcfg.config_for(appid)
         helper = self.config_helper(config, cmdname='stop')
         helper.poststop() # do this anyway
@@ -458,15 +458,15 @@
         except OSError:
             # already removed by twistd
             pass
-        print 'application %s stopped' % appid
+        print 'instance %s stopped' % appid
 
 
-class RestartApplicationCommand(StartApplicationCommand,
-                                StopApplicationCommand):
-    """Restart the given applications.
+class RestartInstanceCommand(StartInstanceCommand,
+                                StopInstanceCommand):
+    """Restart the given instances.
 
-    <application>...
-      identifiers of the applications to restart. If no application is
+    <instance>...
+      identifiers of the instances to restart. If no instance is
       given, restart them all.
     """
     name = 'restart'
@@ -476,18 +476,18 @@
         regdir = cwcfg.registry_dir()
         if not isfile(join(regdir, 'startorder')) or len(args) <= 1:
             # no specific startorder
-            super(RestartApplicationCommand, self).run_args(args, askconfirm)
+            super(RestartInstanceCommand, self).run_args(args, askconfirm)
             return
         print ('some specific start order is specified, will first stop all '
-               'applications then restart them.')
+               'instances then restart them.')
         # get instances in startorder
         stopped = []
         for appid in args:
             if askconfirm:
                 print '*'*72
-                if not confirm('%s application %r ?' % (self.name, appid)):
+                if not confirm('%s instance %r ?' % (self.name, appid)):
                     continue
-            self.stop_application(appid)
+            self.stop_instance(appid)
             stopped.append(appid)
         forkcmd = [w for w in sys.argv if not w in args]
         forkcmd[1] = 'start'
@@ -497,46 +497,46 @@
             if status:
                 sys.exit(status)
 
-    def restart_application(self, appid):
-        self.stop_application(appid)
-        if self.start_application(appid):
-            print 'application %s %s' % (appid, self.actionverb)
+    def restart_instance(self, appid):
+        self.stop_instance(appid)
+        if self.start_instance(appid):
+            print 'instance %s %s' % (appid, self.actionverb)
 
 
-class ReloadConfigurationCommand(RestartApplicationCommand):
-    """Reload the given applications. This command is equivalent to a
+class ReloadConfigurationCommand(RestartInstanceCommand):
+    """Reload the given instances. This command is equivalent to a
     restart for now.
 
-    <application>...
-      identifiers of the applications to reload. If no application is
+    <instance>...
+      identifiers of the instances to reload. If no instance is
       given, reload them all.
     """
     name = 'reload'
 
-    def reload_application(self, appid):
-        self.restart_application(appid)
+    def reload_instance(self, appid):
+        self.restart_instance(appid)
 
 
-class StatusCommand(ApplicationCommand):
-    """Display status information about the given applications.
+class StatusCommand(InstanceCommand):
+    """Display status information about the given instances.
 
-    <application>...
-      identifiers of the applications to status. If no application is
-      given, get status information about all registered applications.
+    <instance>...
+      identifiers of the instances to status. If no instance is
+      given, get status information about all registered instances.
     """
     name = 'status'
     options = ()
 
     @staticmethod
-    def status_application(appid):
-        """print running status information for an application"""
+    def status_instance(appid):
+        """print running status information for an instance"""
         for mode in cwcfg.possible_configurations(appid):
             config = cwcfg.config_for(appid, mode)
             print '[%s-%s]' % (appid, mode),
             try:
                 pidf = config['pid-file']
             except KeyError:
-                print 'buggy application, pid file not specified'
+                print 'buggy instance, pid file not specified'
                 continue
             if not exists(pidf):
                 print "doesn't seem to be running"
@@ -551,22 +551,22 @@
             print "running with pid %s" % (pid)
 
 
-class UpgradeApplicationCommand(ApplicationCommandFork,
-                                StartApplicationCommand,
-                                StopApplicationCommand):
-    """Upgrade an application after cubicweb and/or component(s) upgrade.
+class UpgradeInstanceCommand(InstanceCommandFork,
+                                StartInstanceCommand,
+                                StopInstanceCommand):
+    """Upgrade an instance after cubicweb and/or component(s) upgrade.
 
     For repository update, you will be prompted for a login / password to use
     to connect to the system database.  For some upgrades, the given user
     should have create or alter table permissions.
 
-    <application>...
-      identifiers of the applications to upgrade. If no application is
+    <instance>...
+      identifiers of the instances to upgrade. If no instance is
       given, upgrade them all.
     """
     name = 'upgrade'
     actionverb = 'upgraded'
-    options = ApplicationCommand.options + (
+    options = InstanceCommand.options + (
         ('force-componant-version',
          {'short': 't', 'type' : 'csv', 'metavar': 'cube1=X.Y.Z,cube2=X.Y.Z',
           'default': None,
@@ -584,7 +584,7 @@
         ('nostartstop',
          {'short': 'n', 'action' : 'store_true',
           'default': False,
-          'help': 'don\'t try to stop application before migration and to restart it after.'}),
+          'help': 'don\'t try to stop instance before migration and to restart it after.'}),
 
         ('verbosity',
          {'short': 'v', 'type' : 'int', 'metavar': '<0..2>',
@@ -595,7 +595,7 @@
         ('backup-db',
          {'short': 'b', 'type' : 'yn', 'metavar': '<y or n>',
           'default': None,
-          'help': "Backup the application database before upgrade.\n"\
+          'help': "Backup the instance database before upgrade.\n"\
           "If the option is ommitted, confirmation will be ask.",
           }),
 
@@ -610,25 +610,24 @@
         )
 
     def ordered_instances(self):
-        # need this since mro return StopApplicationCommand implementation
-        return ApplicationCommand.ordered_instances(self)
+        # need this since mro return StopInstanceCommand implementation
+        return InstanceCommand.ordered_instances(self)
 
-    def upgrade_application(self, appid):
+    def upgrade_instance(self, appid):
+        print '\n' + underline_title('Upgrading the instance %s' % appid)
         from logilab.common.changelog import Version
         config = cwcfg.config_for(appid)
-        config.creating = True # notice we're not starting the server
+        config.repairing = True # notice we're not starting the server
         config.verbosity = self.config.verbosity
         try:
             config.set_sources_mode(self.config.ext_sources or ('migration',))
         except AttributeError:
             # not a server config
             pass
-        # get application and installed versions for the server and the componants
-        print 'getting versions configuration from the repository...'
+        # get instance and installed versions for the server and the componants
         mih = config.migration_handler()
         repo = mih.repo_connect()
         vcconf = repo.get_versions()
-        print 'done'
         if self.config.force_componant_version:
             packversions = {}
             for vdef in self.config.force_componant_version:
@@ -654,13 +653,13 @@
         if cubicwebversion > applcubicwebversion:
             toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion))
         if not self.config.fs_only and not toupgrade:
-            print 'no software migration needed for application %s' % appid
+            print '-> no software migration needed for instance %s.' % appid
             return
         for cube, fromversion, toversion in toupgrade:
-            print '**** %s migration %s -> %s' % (cube, fromversion, toversion)
+            print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube)
         # only stop once we're sure we have something to do
         if not (cwcfg.mode == 'dev' or self.config.nostartstop):
-            self.stop_application(appid)
+            self.stop_instance(appid)
         # run cubicweb/componants migration scripts
         mih.migrate(vcconf, reversed(toupgrade), self.config)
         # rewrite main configuration file
@@ -675,15 +674,15 @@
         errors = config.i18ncompile(langs)
         if errors:
             print '\n'.join(errors)
-            if not confirm('error while compiling message catalogs, '
+            if not confirm('Error while compiling message catalogs, '
                            'continue anyway ?'):
-                print 'migration not completed'
+                print '-> migration not completed.'
                 return
         mih.shutdown()
         print
-        print 'application migrated'
+        print '-> instance migrated.'
         if not (cwcfg.mode == 'dev' or self.config.nostartstop):
-            self.start_application(appid)
+            self.start_instance(appid)
         print
 
 
@@ -693,11 +692,11 @@
     argument may be given corresponding to a file containing commands to
     execute in batch mode.
 
-    <application>
-      the identifier of the application to connect.
+    <instance>
+      the identifier of the instance to connect.
     """
     name = 'shell'
-    arguments = '<application> [batch command file]'
+    arguments = '<instance> [batch command file]'
     options = (
         ('system-only',
          {'short': 'S', 'action' : 'store_true',
@@ -717,7 +716,7 @@
 
         )
     def run(self, args):
-        appid = pop_arg(args, 99, msg="No application specified !")
+        appid = pop_arg(args, 99, msg="No instance specified !")
         config = cwcfg.config_for(appid)
         if self.config.ext_sources:
             assert not self.config.system_only
@@ -736,18 +735,18 @@
         mih.shutdown()
 
 
-class RecompileApplicationCatalogsCommand(ApplicationCommand):
-    """Recompile i18n catalogs for applications.
+class RecompileInstanceCatalogsCommand(InstanceCommand):
+    """Recompile i18n catalogs for instances.
 
-    <application>...
-      identifiers of the applications to consider. If no application is
-      given, recompile for all registered applications.
+    <instance>...
+      identifiers of the instances to consider. If no instance is
+      given, recompile for all registered instances.
     """
     name = 'i18ninstance'
 
     @staticmethod
-    def i18ninstance_application(appid):
-        """recompile application's messages catalogs"""
+    def i18ninstance_instance(appid):
+        """recompile instance's messages catalogs"""
         config = cwcfg.config_for(appid)
         try:
             config.bootstrap_cubes()
@@ -756,8 +755,8 @@
             if ex.errno != errno.ENOENT:
                 raise
             # bootstrap_cubes files doesn't exist
-            # set creating to notify this is not a regular start
-            config.creating = True
+            # notify this is not a regular start
+            config.repairing = True
             # create an in-memory repository, will call config.init_cubes()
             config.repository()
         except AttributeError:
@@ -791,16 +790,16 @@
             print cube
 
 register_commands((ListCommand,
-                   CreateApplicationCommand,
-                   DeleteApplicationCommand,
-                   StartApplicationCommand,
-                   StopApplicationCommand,
-                   RestartApplicationCommand,
+                   CreateInstanceCommand,
+                   DeleteInstanceCommand,
+                   StartInstanceCommand,
+                   StopInstanceCommand,
+                   RestartInstanceCommand,
                    ReloadConfigurationCommand,
                    StatusCommand,
-                   UpgradeApplicationCommand,
+                   UpgradeInstanceCommand,
                    ShellCommand,
-                   RecompileApplicationCatalogsCommand,
+                   RecompileInstanceCatalogsCommand,
                    ListInstancesCommand, ListCubesCommand,
                    ))
 
--- a/cwvreg.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/cwvreg.py	Fri Jul 31 14:25:30 2009 +0200
@@ -9,6 +9,7 @@
 _ = unicode
 
 from logilab.common.decorators import cached, clear_cache
+from logilab.common.deprecation import  obsolete
 
 from rql import RQLHelper
 
@@ -39,7 +40,26 @@
 
 
 class CubicWebRegistry(VRegistry):
-    """extend the generic VRegistry with some cubicweb specific stuff"""
+    """Central registry for the cubicweb instance, extending the generic
+    VRegistry with some cubicweb specific stuff.
+
+    This is one of the central object in cubicweb instance, coupling
+    dynamically loaded objects with the schema and the configuration objects.
+
+    It specializes the VRegistry by adding some convenience methods to access to
+    stored objects. Currently we have the following registries of objects known
+    by the web instance (library may use some others additional registries):
+
+    * etypes
+    * views
+    * components
+    * actions
+    * forms
+    * formrenderers
+    * controllers, which are directly plugged into the application
+      object to handle request publishing XXX to merge with views
+    * contentnavigation XXX to merge with components? to kill?
+    """
 
     def __init__(self, config, debug=None, initlog=True):
         if initlog:
@@ -71,7 +91,7 @@
             self.register_property(key, **propdef)
 
     def set_schema(self, schema):
-        """set application'schema and load application objects"""
+        """set instance'schema and load application objects"""
         self.schema = schema
         clear_cache(self, 'rqlhelper')
         # now we can load application's web objects
@@ -175,7 +195,7 @@
         """
         etype = str(etype)
         if etype == 'Any':
-            return self.select(self.registry_objects('etypes', 'Any'), 'Any')
+            return self.select('etypes', 'Any', 'Any')
         eschema = self.schema.eschema(etype)
         baseschemas = [eschema] + eschema.ancestors()
         # browse ancestors from most specific to most generic and
@@ -186,42 +206,47 @@
             except KeyError:
                 btype = str(baseschema)
             try:
-                cls = self.select(self.registry_objects('etypes', btype), etype)
+                cls = self.select('etypes', btype, etype)
                 break
             except ObjectNotFound:
                 pass
         else:
             # no entity class for any of the ancestors, fallback to the default
             # one
-            cls = self.select(self.registry_objects('etypes', 'Any'), etype)
+            cls = self.select('etypes', 'Any', etype)
         return cls
 
-    def render(self, registry, oid, req, **context):
-        """select an object in a given registry and render it
-
-        - registry: the registry's name
-        - oid : the view to call
-        - req : the HTTP request
+    def render(self, __oid, req, __fallback_oid=None, __registry='views',
+               rset=None, **kwargs):
+        """select object, or fallback object if specified and the first one
+        isn't selectable, then render it
         """
-        objclss = self.registry_objects(registry, oid)
         try:
-            rset = context.pop('rset')
-        except KeyError:
-            rset = None
-        selected = self.select(objclss, req, rset, **context)
-        return selected.render(**context)
+            obj = self.select(__registry, __oid, req, rset=rset, **kwargs)
+        except NoSelectableObject:
+            if __fallback_oid is None:
+                raise
+            obj = self.select(__registry, __fallback_oid, req, rset=rset,
+                              **kwargs)
+        return obj.render(**kwargs)
 
     def main_template(self, req, oid='main-template', **context):
         """display query by calling the given template (default to main),
         and returning the output as a string instead of requiring the [w]rite
         method as argument
         """
-        res = self.render('views', oid, req, **context)
+        res = self.render(oid, req, **context)
         if isinstance(res, unicode):
             return res.encode(req.encoding)
         assert isinstance(res, str)
         return res
 
+    def select_vobject(self, registry, oid, *args, **kwargs):
+        selected = self.select_object(registry, oid, *args, **kwargs)
+        if selected and selected.propval('visible'):
+            return selected
+        return None
+
     def possible_vobjects(self, registry, *args, **kwargs):
         """return an ordered list of possible app objects in a given registry,
         supposing they support the 'visible' and 'order' properties (as most
@@ -231,9 +256,9 @@
                                   key=lambda x: x.propval('order'))
                 if x.propval('visible')]
 
-    def possible_actions(self, req, rset, **kwargs):
+    def possible_actions(self, req, rset=None, **kwargs):
         if rset is None:
-            actions = self.possible_vobjects('actions', req, rset, **kwargs)
+            actions = self.possible_vobjects('actions', req, rset=rset, **kwargs)
         else:
             actions = rset.possible_actions(**kwargs) # cached implementation
         result = {}
@@ -241,7 +266,7 @@
             result.setdefault(action.category, []).append(action)
         return result
 
-    def possible_views(self, req, rset, **kwargs):
+    def possible_views(self, req, rset=None, **kwargs):
         """return an iterator on possible views for this result set
 
         views returned are classes, not instances
@@ -250,50 +275,34 @@
             if vid[0] == '_':
                 continue
             try:
-                view = self.select(views, req, rset, **kwargs)
+                view = self.select_best(views, req, rset=rset, **kwargs)
                 if view.linkable():
                     yield view
             except NoSelectableObject:
                 continue
             except Exception:
-                self.exception('error while trying to list possible %s views for %s',
+                self.exception('error while trying to select %s view for %s',
                                vid, rset)
 
-    def view(self, __vid, req, rset=None, __fallback_vid=None, **kwargs):
-        """shortcut to self.vreg.render method avoiding to pass self.req"""
-        try:
-            view = self.select_view(__vid, req, rset, **kwargs)
-        except NoSelectableObject:
-            if __fallback_vid is None:
-                raise
-            view = self.select_view(__fallback_vid, req, rset, **kwargs)
-        return view.render(**kwargs)
-
+    @obsolete("use .select_object('boxes', ...)")
     def select_box(self, oid, *args, **kwargs):
         """return the most specific view according to the result set"""
-        try:
-            return self.select_object('boxes', oid, *args, **kwargs)
-        except NoSelectableObject:
-            return
+        return self.select_object('boxes', oid, *args, **kwargs)
 
+    @obsolete("use .select_object('components', ...)")
+    def select_component(self, cid, *args, **kwargs):
+        """return the most specific component according to the result set"""
+        return self.select_object('components', cid, *args, **kwargs)
+
+    @obsolete("use .select_object('actions', ...)")
     def select_action(self, oid, *args, **kwargs):
         """return the most specific view according to the result set"""
-        try:
-            return self.select_object('actions', oid, *args, **kwargs)
-        except NoSelectableObject:
-            return
+        return self.select_object('actions', oid, *args, **kwargs)
 
-    def select_component(self, cid, *args, **kwargs):
-        """return the most specific component according to the result set"""
-        try:
-            return self.select_object('components', cid, *args, **kwargs)
-        except (NoSelectableObject, ObjectNotFound):
-            return
-
+    @obsolete("use .select('views', ...)")
     def select_view(self, __vid, req, rset=None, **kwargs):
         """return the most specific view according to the result set"""
-        views = self.registry_objects('views', __vid)
-        return self.select(views, req, rset, **kwargs)
+        return self.select('views', __vid, req, rset=rset, **kwargs)
 
     # properties handling #####################################################
 
@@ -388,7 +397,7 @@
     """special registry to be used when an application has to deal with
     connections to differents repository. This class add some additional wrapper
     trying to hide buggy class attributes since classes are not designed to be
-    shared.
+    shared among multiple registries.
     """
     def etype_class(self, etype):
         """return an entity class for the given entity type.
@@ -401,25 +410,27 @@
         usercls.e_schema = self.schema.eschema(etype)
         return usercls
 
-    def select(self, vobjects, *args, **kwargs):
+    def select_best(self, vobjects, *args, **kwargs):
         """return an instance of the most specific object according
         to parameters
 
-        raise NoSelectableObject if not object apply
+        raise NoSelectableObject if no object apply
         """
-        for vobject in vobjects:
-            vobject.vreg = self
-            vobject.schema = self.schema
-            vobject.config = self.config
-        selected = super(MulCnxCubicWebRegistry, self).select(vobjects, *args,
-                                                              **kwargs)
+        for vobjectcls in vobjects:
+            self._fix_cls_attrs(vobjectcls)
+        selected = super(MulCnxCubicWebRegistry, self).select_best(
+            vobjects, *args, **kwargs)
         # redo the same thing on the instance so it won't use equivalent class
         # attributes (which may change)
-        selected.vreg = self
-        selected.schema = self.schema
-        selected.config = self.config
+        self._fix_cls_attrs(selected)
         return selected
 
+    def _fix_cls_attrs(self, vobject):
+        vobject.vreg = self
+        vobject.schema = self.schema
+        vobject.config = self.config
+
+
 from datetime import datetime, date, time, timedelta
 
 YAMS_TO_PY = {
--- a/dbapi.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/dbapi.py	Fri Jul 31 14:25:30 2009 +0200
@@ -13,6 +13,7 @@
 
 from logging import getLogger
 from time import time, clock
+from itertools import count
 
 from logilab.common.logging_ext import set_log_methods
 from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
@@ -21,6 +22,12 @@
 
 _MARKER = object()
 
+def _fake_property_value(self, name):
+    try:
+        return super(dbapi.DBAPIRequest, self).property_value(name)
+    except KeyError:
+        return ''
+
 class ConnectionProperties(object):
     def __init__(self, cnxtype=None, lang=None, close=True, log=False):
         self.cnxtype = cnxtype or 'pyro'
@@ -60,7 +67,7 @@
             raise ConnectionError('Could not get repository for %s '
                                   '(not registered in Pyro), '
                                   'you may have to restart your server-side '
-                                  'application' % nsid)
+                                  'instance' % nsid)
         return core.getProxyForURI(uri)
 
 def repo_connect(repo, login, password, cnxprops=None):
@@ -394,7 +401,8 @@
             raise ProgrammingError('Closed connection')
         return self._repo.get_schema()
 
-    def load_vobjects(self, cubes=_MARKER, subpath=None, expand=True, force_reload=None):
+    def load_vobjects(self, cubes=_MARKER, subpath=None, expand=True,
+                      force_reload=None):
         config = self.vreg.config
         if cubes is _MARKER:
             cubes = self._repo.get_cubes()
@@ -422,10 +430,35 @@
             hm, config = self._repo.hm, self._repo.config
             hm.set_schema(hm.schema) # reset structure
             hm.register_system_hooks(config)
-            # application specific hooks
-            if self._repo.config.application_hooks:
+            # instance specific hooks
+            if self._repo.config.instance_hooks:
                 hm.register_hooks(config.load_hooks(self.vreg))
 
+    def use_web_compatible_requests(self, baseurl, sitetitle=None):
+        """monkey patch DBAPIRequest to fake a cw.web.request, so you should
+        able to call html views using rset from a simple dbapi connection.
+
+        You should call `load_vobjects` at some point to register those views.
+        """
+        from cubicweb.web.request import CubicWebRequestBase as cwrb
+        DBAPIRequest.build_ajax_replace_url = cwrb.build_ajax_replace_url.im_func
+        DBAPIRequest.list_form_param = cwrb.list_form_param.im_func
+        DBAPIRequest.property_value = _fake_property_value
+        DBAPIRequest.next_tabindex = count().next
+        DBAPIRequest.form = {}
+        DBAPIRequest.data = {}
+        fake = lambda *args, **kwargs: None
+        DBAPIRequest.relative_path = fake
+        DBAPIRequest.url = fake
+        DBAPIRequest.next_tabindex = fake
+        DBAPIRequest.add_js = fake #cwrb.add_js.im_func
+        DBAPIRequest.add_css = fake #cwrb.add_css.im_func
+        # XXX could ask the repo for it's base-url configuration
+        self.vreg.config.set_option('base-url', baseurl)
+        # XXX why is this needed? if really needed, could be fetched by a query
+        if sitetitle is not None:
+            self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
+
     def source_defs(self):
         """Return the definition of sources used by the repository.
 
--- a/debian/control	Fri Jul 31 14:18:53 2009 +0200
+++ b/debian/control	Fri Jul 31 14:25:30 2009 +0200
@@ -62,7 +62,7 @@
 Architecture: all
 XB-Python-Version: ${python:Versions}
 Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-simplejson (>= 1.3), python-elementtree
-Recommends: python-docutils, python-vobject, fckeditor
+Recommends: python-docutils, python-vobject, fckeditor, python-fyzz
 Description: web interface library for the CubicWeb framework
  CubicWeb is a semantic web application framework.
  .
@@ -76,8 +76,8 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.43.0), python-yams (>= 0.23.0), python-rql (>= 0.22.1)
-Recommends: python-simpletal (>= 4.0), python-lxml
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.41.0), python-yams (>= 0.23.0), python-rql (>= 0.22.1), python-lxml
+Recommends: python-simpletal (>= 4.0)
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
 Description: common library for the CubicWeb framework
--- a/devtools/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -43,7 +43,7 @@
 class TestServerConfiguration(ServerConfiguration):
     mode = 'test'
     set_language = False
-    read_application_schema = False
+    read_instance_schema = False
     bootstrap_schema = False
     init_repository = True
     options = merge_options(ServerConfiguration.options + (
@@ -77,12 +77,12 @@
     def apphome(self):
         if exists(self.appid):
             return abspath(self.appid)
-        # application cube test
+        # cube test
         return abspath('..')
     appdatahome = apphome
 
     def main_config_file(self):
-        """return application's control configuration file"""
+        """return instance's control configuration file"""
         return join(self.apphome, '%s.conf' % self.name)
 
     def instance_md5_version(self):
@@ -149,7 +149,7 @@
         return ('en', 'fr', 'de')
 
     def ext_resources_file(self):
-        """return application's external resources file"""
+        """return instance's external resources file"""
         return join(self.apphome, 'data', 'external_resources')
 
     def pyro_enabled(self):
@@ -235,14 +235,14 @@
         # XXX I'm afraid this test will prevent to run test from a production
         # environment
         self._sources = None
-        # application cube test
+        # instance cube test
         if cube is not None:
             self.apphome = self.cube_dir(cube)
         elif 'web' in os.getcwd().split(os.sep):
             # web test
             self.apphome = join(normpath(join(dirname(__file__), '..')), 'web')
         else:
-            # application cube test
+            # cube test
             self.apphome = abspath('..')
         self.sourcefile = sourcefile
         self.global_set_option('realm', '')
--- a/devtools/_apptest.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/_apptest.py	Fri Jul 31 14:25:30 2009 +0200
@@ -177,7 +177,7 @@
                 self.create_request(rql=rql, **optional_args or {}))
 
     def check_view(self, rql, vid, optional_args, template='main'):
-        """checks if vreg.view() raises an exception in this environment
+        """checks if rendering view raises an exception in this environment
 
         If any exception is raised in this method, it will be considered
         as a TestFailure
@@ -186,7 +186,6 @@
                               template=template, optional_args=optional_args)
 
     def call_view(self, vid, rql, template='main', optional_args=None):
-        """shortcut for self.vreg.view()"""
         assert template
         if optional_args is None:
             optional_args = {}
@@ -196,7 +195,7 @@
 
     def call_edit(self, req):
         """shortcut for self.app.edit()"""
-        controller = self.app.select_controller('edit', req)
+        controller = self.vreg.select('controllers', 'edit', req)
         try:
             controller.publish()
         except Redirect:
@@ -224,7 +223,7 @@
 
     def iter_possible_actions(self, req, rset):
         """returns a list of possible vids for <rql>"""
-        for action in self.vreg.possible_vobjects('actions', req, rset):
+        for action in self.vreg.possible_vobjects('actions', req, rset=rset):
             yield action
 
 class ExistingTestEnvironment(TestEnvironment):
--- a/devtools/apptest.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/apptest.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-"""This module provides misc utilities to test applications
+"""This module provides misc utilities to test instances
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -34,6 +34,14 @@
     def message(self):
         return message_from_string(self.msg)
 
+    @property
+    def subject(self):
+        return self.message.get('Subject')
+
+    @property
+    def content(self):
+        return self.message.get_payload(decode=True)
+
     def __repr__(self):
         return '<Email to %s with subject %s>' % (','.join(self.recipients),
                                                   self.message.get('Subject'))
@@ -51,7 +59,7 @@
 
 
 def get_versions(self, checkversions=False):
-    """return the a dictionary containing cubes used by this application
+    """return the a dictionary containing cubes used by this instance
     as key with their version as value, including cubicweb version. This is a
     public method, not requiring a session id.
 
@@ -219,18 +227,18 @@
         return sorted((a.id, a.__class__) for a in self.vreg.possible_views(req, rset))
 
     def pactions(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
-        return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset)
+        return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset)
                 if a.category not in skipcategories]
 
     def pactions_by_cats(self, req, rset, categories=('addrelated',)):
-        return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset)
+        return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset)
                 if a.category in categories]
 
     paddrelactions = deprecated_function(pactions_by_cats)
 
     def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
         res = {}
-        for a in self.vreg.possible_vobjects('actions', req, rset):
+        for a in self.vreg.possible_vobjects('actions', req, rset=rset):
             if a.category not in skipcategories:
                 res.setdefault(a.category, []).append(a.__class__)
         return res
@@ -241,7 +249,7 @@
         dump = simplejson.dumps
         args = [dump(arg) for arg in args]
         req = self.request(fname=fname, pageid='123', arg=args)
-        ctrl = self.env.app.select_controller('json', req)
+        ctrl = self.vreg.select('controllers', 'json', req)
         return ctrl.publish(), req
 
     # default test setup and teardown #########################################
@@ -286,7 +294,7 @@
         def setUp(self):
             super(ControllerTC, self).setUp()
             self.req = self.request()
-            self.ctrl = self.env.app.select_controller('edit', self.req)
+            self.ctrl = self.vreg.select('controllers', 'edit', self.req)
 
         def publish(self, req):
             assert req is self.ctrl.req
@@ -300,7 +308,7 @@
 
         def expect_redirect_publish(self, req=None):
             if req is not None:
-                self.ctrl = self.env.app.select_controller('edit', req)
+                self.ctrl = self.vreg.select('controllers', 'edit', req)
             else:
                 req = self.req
             try:
@@ -398,7 +406,7 @@
         rset.vreg = self.vreg
         rset.req = self.session
         # call to set_pool is necessary to avoid pb when using
-        # application entities for convenience
+        # instance entities for convenience
         self.session.set_pool()
         return rset
 
--- a/devtools/devctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/devctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -12,7 +12,7 @@
 from datetime import datetime
 from os import mkdir, chdir
 from os.path import join, exists, abspath, basename, normpath, split, isdir
-
+from warnings import warn
 
 from logilab.common import STD_BLACKLIST
 from logilab.common.modutils import get_module_files
@@ -254,14 +254,13 @@
         if args:
             raise BadCommandUsage('Too much arguments')
         import shutil
-        from tempfile import mktemp
+        import tempfile
         import yams
         from logilab.common.fileutils import ensure_fs_mode
         from logilab.common.shellutils import globfind, find, rm
         from cubicweb.common.i18n import extract_from_tal, execute
-        tempdir = mktemp()
-        mkdir(tempdir)
-        potfiles = [join(I18NDIR, 'entities.pot')]
+        tempdir = tempdir.mkdtemp()
+        potfiles = [join(I18NDIR, 'static-messages.pot')]
         print '-> extract schema messages.'
         schemapot = join(tempdir, 'schema.pot')
         potfiles.append(schemapot)
@@ -328,8 +327,8 @@
 
 
 def update_cubes_catalogs(cubes):
-    toedit = []
     for cubedir in cubes:
+        toedit = []
         if not isdir(cubedir):
             print '-> ignoring %s that is not a directory.' % cubedir
             continue
@@ -338,27 +337,34 @@
         except Exception:
             import traceback
             traceback.print_exc()
-            print '-> Error while updating catalogs for cube', cubedir
-    # instructions pour la suite
-    print '-> regenerated this cube\'s .po catalogs.'
-    print '\nYou can now edit the following files:'
-    print '* ' + '\n* '.join(toedit)
-    print 'when you are done, run "cubicweb-ctl i18ninstance yourinstance".'
+            print '-> error while updating catalogs for cube', cubedir
+        else:
+            # instructions pour la suite
+            print '-> regenerated .po catalogs for cube %s.' % cubedir
+            print '\nYou can now edit the following files:'
+            print '* ' + '\n* '.join(toedit)
+            print ('When you are done, run "cubicweb-ctl i18ninstance '
+                   '<yourinstance>" to see changes in your instances.')
 
 def update_cube_catalogs(cubedir):
     import shutil
-    from tempfile import mktemp
+    import tempfile
     from logilab.common.fileutils import ensure_fs_mode
     from logilab.common.shellutils import find, rm
     from cubicweb.common.i18n import extract_from_tal, execute
     toedit = []
     cube = basename(normpath(cubedir))
-    tempdir = mktemp()
-    mkdir(tempdir)
+    tempdir = tempfile.mkdtemp()
     print underline_title('Updating i18n catalogs for cube %s' % cube)
     chdir(cubedir)
-    potfiles = [join('i18n', scfile) for scfile in ('entities.pot',)
-                if exists(join('i18n', scfile))]
+    if exists(join('i18n', 'entities.pot')):
+        warn('entities.pot is deprecated, rename file to static-messages.pot (%s)'
+             % join('i18n', 'entities.pot'), DeprecationWarning)
+        potfiles = [join('i18n', 'entities.pot')]
+    elif exists(join('i18n', 'static-messages.pot')):
+        potfiles = [join('i18n', 'static-messages.pot')]
+    else:
+        potfiles = []
     print '-> extract schema messages'
     schemapot = join(tempdir, 'schema.pot')
     potfiles.append(schemapot)
--- a/devtools/fake.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/fake.py	Fri Jul 31 14:25:30 2009 +0200
@@ -49,8 +49,7 @@
     _registries = {
         'controllers' : [Mock(id='view'), Mock(id='login'),
                          Mock(id='logout'), Mock(id='edit')],
-        'views' : [Mock(id='primary'), Mock(id='secondary'),
-                         Mock(id='oneline'), Mock(id='list')],
+        'views' : [Mock(id='primary'), Mock(id='oneline'), Mock(id='list')],
         }
 
     def registry_objects(self, name, oid=None):
@@ -88,12 +87,12 @@
         return None
 
     def base_url(self):
-        """return the root url of the application"""
+        """return the root url of the instance"""
         return BASE_URL
 
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
-        to the application's root, but some other normalization may be needed
+        to the instance's root, but some other normalization may be needed
         so that the returned path may be used to compare to generated urls
         """
         if self._url.startswith(BASE_URL):
@@ -154,6 +153,25 @@
         return self.execute(*args, **kwargs)
 
 
+# class FakeRequestNoCnx(FakeRequest):
+#     def get_session_data(self, key, default=None, pop=False):
+#         """return value associated to `key` in session data"""
+#         if pop:
+#             return self._session_data.pop(key, default)
+#         else:
+#             return self._session_data.get(key, default)
+
+#     def set_session_data(self, key, value):
+#         """set value associated to `key` in session data"""
+#         self._session_data[key] = value
+
+#     def del_session_data(self, key):
+#         try:
+#             del self._session_data[key]
+#         except KeyError:
+#             pass
+
+
 class FakeUser(object):
     login = 'toto'
     eid = 0
--- a/devtools/fill.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/fill.py	Fri Jul 31 14:25:30 2009 +0200
@@ -225,7 +225,7 @@
     :param etype: the entity's type
 
     :type schema: cubicweb.schema.Schema
-    :param schema: the application schema
+    :param schema: the instance schema
 
     :type entity_num: int
     :param entity_num: the number of entities to insert
@@ -325,7 +325,7 @@
 def make_relations_queries(schema, edict, cursor, ignored_relations=(),
                            existingrels=None):
     """returns a list of generated RQL queries for relations
-    :param schema: The application schema
+    :param schema: The instance schema
 
     :param e_dict: mapping between etypes and eids
 
--- a/devtools/pkginfo.py	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-"""distutils / __pkginfo__ helpers for cubicweb applications
-
-:organization: Logilab
-:copyright: 2001-2009 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
-"""
-
-import os
-from os.path import isdir, join
-
-
-def get_distutils_datafiles(cube, i18n=True, recursive=False):
-    """
-    :param cube: application cube's name
-    """
-    data_files = []
-    data_files += get_basepyfiles(cube)
-    data_files += get_webdatafiles(cube)
-    if i18n:
-        data_files += get_i18nfiles(cube)
-    data_files += get_viewsfiles(cube, recursive=recursive)
-    data_files += get_migrationfiles(cube)
-    data_files += get_schemafiles(cube)
-    return data_files
-
-
-
-## listdir filter funcs ################################################
-def nopyc_and_nodir(fname):
-    if isdir(fname) or fname.endswith('.pyc') or fname.endswith('~'):
-        return False
-    return True
-
-def no_version_control(fname):
-    if fname in ('CVS', '.svn', '.hg'):
-        return False
-    if fname.endswith('~'):
-        return False
-    return True
-
-def basepy_files(fname):
-    if fname.endswith('.py') and fname != 'setup.py':
-        return True
-    return False
-
-def chain(*filters):
-    def newfilter(fname):
-        for filterfunc in filters:
-            if not filterfunc(fname):
-                return False
-        return True
-    return newfilter
-
-def listdir_with_path(path='.', filterfunc=None):
-    if filterfunc:
-        return [join(path, fname) for fname in os.listdir(path) if filterfunc(join(path, fname))]
-    else:
-        return [join(path, fname) for fname in os.listdir(path)]
-
-
-## data_files helpers ##################################################
-CUBES_DIR = join('share', 'cubicweb', 'cubes')
-
-def get_i18nfiles(cube):
-    """returns i18n files in a suitable format for distutils's
-    data_files parameter
-    """
-    i18ndir = join(CUBES_DIR, cube, 'i18n')
-    potfiles = [(i18ndir, listdir_with_path('i18n', chain(no_version_control, nopyc_and_nodir)))]
-    return potfiles
-
-
-def get_viewsfiles(cube, recursive=False):
-    """returns views files in a suitable format for distutils's
-    data_files parameter
-
-    :param recursive: include views' subdirs recursively if True
-    """
-    if recursive:
-        datafiles = []
-        for dirpath, dirnames, filenames in os.walk('views'):
-            filenames = [join(dirpath, fname) for fname in filenames
-                         if nopyc_and_nodir(join(dirpath, fname))]
-            dirpath = join(CUBES_DIR, cube, dirpath)
-            datafiles.append((dirpath, filenames))
-        return datafiles
-    else:
-        viewsdir = join(CUBES_DIR, cube, 'views')
-        return [(viewsdir,
-                 listdir_with_path('views', filterfunc=nopyc_and_nodir))]
-
-
-def get_basepyfiles(cube):
-    """returns cube's base python scripts (tali18n.py, etc.)
-    in a suitable format for distutils's data_files parameter
-    """
-    return [(join(CUBES_DIR, cube),
-             [fname for fname in os.listdir('.')
-              if fname.endswith('.py') and fname != 'setup.py'])]
-
-
-def get_webdatafiles(cube):
-    """returns web's data files (css, png, js, etc.) in a suitable
-    format for distutils's data_files parameter
-    """
-    return [(join(CUBES_DIR, cube, 'data'),
-             listdir_with_path('data', filterfunc=no_version_control))]
-
-
-def get_migrationfiles(cube):
-    """returns cube's migration scripts
-    in a suitable format for distutils's data_files parameter
-    """
-    return [(join(CUBES_DIR, cube, 'migration'),
-             listdir_with_path('migration', no_version_control))]
-
-
-def get_schemafiles(cube):
-    """returns cube's schema files
-    in a suitable format for distutils's data_files parameter
-    """
-    return [(join(CUBES_DIR, cube, 'schema'),
-             listdir_with_path('schema', no_version_control))]
-
-
--- a/devtools/stresstester.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/stresstester.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-""" Usage: %s [OPTIONS] <application id> <queries file>
+""" Usage: %s [OPTIONS] <instance id> <queries file>
 
 Stress test a CubicWeb repository
 
@@ -151,8 +151,8 @@
         user = raw_input('login: ')
     if password is None:
         password = getpass('password: ')
-    from cubicweb.cwconfig import application_configuration
-    config = application_configuration(args[0])
+    from cubicweb.cwconfig import instance_configuration
+    config = instance_configuration(args[0])
     # get local access to the repository
     print "Creating repo", prof_file
     repo = Repository(config, prof_file)
--- a/devtools/test/data/bootstrap_cubes	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/test/data/bootstrap_cubes	Fri Jul 31 14:25:30 2009 +0200
@@ -1,1 +1,1 @@
-person, comment
+person
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/data/schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,20 @@
+"""
+
+:organization: Logilab
+:copyright: 2001-2009 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
+"""
+from yams.buildobjs import EntityType, SubjectRelation, String, Int, Date
+
+from cubes.person.schema import Person
+
+Person.add_relation(Date(), 'birthday')
+
+class Bug(EntityType):
+    title = String(maxsize=64, required=True, fulltextindexed=True)
+    severity = String(vocabulary=('important', 'normal', 'minor'), default='normal')
+    cost = Int()
+    description	= String(maxsize=4096, fulltextindexed=True)
+    identical_to = SubjectRelation('Bug', symetric=True)
+
--- a/devtools/test/data/schema/Bug.sql	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-title		ivarchar(64) not null
-state		CHOICE('open', 'rejected', 'validation pending', 'resolved') default 'open'
-severity	CHOICE('important', 'normal', 'minor') default 'normal'
-cost 		integer
-description	ivarchar(4096)
--- a/devtools/test/data/schema/Project.sql	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-name		ivarchar(64) not null
-summary		ivarchar(128)	
-vcsurl		varchar(256)
-reporturl	varchar(256)
-description	ivarchar(1024)
-url		varchar(128)
--- a/devtools/test/data/schema/Story.sql	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-title		ivarchar(64) not null
-state		CHOICE('open', 'rejected', 'validation pending', 'resolved') default 'open'
-priority	CHOICE('minor', 'normal', 'important') default 'normal'
-cost	        integer
-description	ivarchar(4096)
--- a/devtools/test/data/schema/Version.sql	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-num	varchar(16) not null
-diem	date	
-status 	CHOICE('planned', 'dev', 'published') default 'planned'
--- a/devtools/test/data/schema/custom.py	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-"""
-
-:organization: Logilab
-:copyright: 2001-2009 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
-"""
-Person = import_erschema('Person')
-Person.add_relation(Date(), 'birthday')
--- a/devtools/test/data/schema/relations.rel	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-Bug concerns Project inline
-Story concerns Project inline
-
-Bug corrected_in Version inline CONSTRAINT E concerns P, X version_of P
-Story done_in Version inline CONSTRAINT E concerns P, X version_of P
-
-Bug   identical_to Bug   symetric
-Bug   identical_to Story symetric
-Story identical_to Story symetric
-
-Story depends_on Story
-Story depends_on Bug
-Bug   depends_on Story
-Bug   depends_on Bug
-
-Bug     see_also Bug	 symetric
-Bug	see_also Story	 symetric
-Bug	see_also Project symetric
-Story	see_also Story	 symetric
-Story	see_also Project symetric
-Project see_also Project symetric
-
-Project uses Project
-
-Version version_of Project inline
-Version todo_by CWUser
-
-Comment about Bug inline
-Comment about Story inline
-Comment about Comment inline
-
-CWUser interested_in Project
-
--- a/devtools/testlib.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/devtools/testlib.py	Fri Jul 31 14:25:30 2009 +0200
@@ -44,7 +44,7 @@
     # compute how many entities by type we need to be able to satisfy relation constraint
     relmap = {}
     for rschema in schema.relations():
-        if rschema.meta or rschema.is_final(): # skip meta relations
+        if rschema.is_final():
             continue
         for subj, obj in rschema.iter_rdefs():
             card = rschema.rproperty(subj, obj, 'cardinality')
@@ -172,7 +172,8 @@
         return validator.parse_string(output.strip())
 
 
-    def view(self, vid, rset, req=None, template='main-template', **kwargs):
+    def view(self, vid, rset=None, req=None, template='main-template',
+             **kwargs):
         """This method tests the view `vid` on `rset` using `template`
 
         If no error occured while rendering the view, the HTML is analyzed
@@ -183,7 +184,8 @@
         """
         req = req or rset and rset.req or self.request()
         req.form['vid'] = vid
-        view = self.vreg.select_view(vid, req, rset, **kwargs)
+        kwargs['rset'] = rset
+        view = self.vreg.select('views', vid, req, **kwargs)
         # set explicit test description
         if rset is not None:
             self.set_description("testing %s, mod=%s (%s)" % (
@@ -194,9 +196,11 @@
         if template is None: # raw view testing, no template
             viewfunc = view.render
         else:
-            templateview = self.vreg.select_view(template, req, rset, view=view, **kwargs)
             kwargs['view'] = view
-            viewfunc = lambda **k: self.vreg.main_template(req, template, **kwargs)
+            templateview = self.vreg.select('views', template, req, **kwargs)
+            viewfunc = lambda **k: self.vreg.main_template(req, template,
+                                                           **kwargs)
+        kwargs.pop('rset')
         return self._test_view(viewfunc, view, template, kwargs)
 
 
@@ -278,7 +282,7 @@
                      and not issubclass(view, NotificationView)]
             if views:
                 try:
-                    view = self.vreg.select(views, req, rset)
+                    view = self.vreg.select_best(views, req, rset=rset)
                     if view.linkable():
                         yield view
                     else:
@@ -291,13 +295,13 @@
     def list_actions_for(self, rset):
         """returns the list of actions that can be applied on `rset`"""
         req = rset.req
-        for action in self.vreg.possible_objects('actions', req, rset):
+        for action in self.vreg.possible_objects('actions', req, rset=rset):
             yield action
 
     def list_boxes_for(self, rset):
         """returns the list of boxes that can be applied on `rset`"""
         req = rset.req
-        for box in self.vreg.possible_objects('boxes', req, rset):
+        for box in self.vreg.possible_objects('boxes', req, rset=rset):
             yield box
 
     def list_startup_views(self):
@@ -383,9 +387,9 @@
                                            requestcls=testclass.requestcls)
     vreg = env.vreg
     vreg._selected = {}
-    orig_select = vreg.__class__.select
-    def instr_select(self, *args, **kwargs):
-        selected = orig_select(self, *args, **kwargs)
+    orig_select_best = vreg.__class__.select_best
+    def instr_select_best(self, *args, **kwargs):
+        selected = orig_select_best(self, *args, **kwargs)
         try:
             self._selected[selected.__class__] += 1
         except KeyError:
@@ -393,7 +397,7 @@
         except AttributeError:
             pass # occurs on vreg used to restore database
         return selected
-    vreg.__class__.select = instr_select
+    vreg.__class__.select_best = instr_select_best
 
 def print_untested_objects(testclass, skipregs=('hooks', 'etypes')):
     vreg = testclass._env.vreg
--- a/doc/book/en/.templates/layout.html	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/.templates/layout.html	Fri Jul 31 14:25:30 2009 +0200
@@ -4,7 +4,7 @@
 {%- endblock %}
 {%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
 {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
-{%- macro relbar %}
+{%- macro relbar() %}
     <div class="related">
       <h3>Navigation</h3>
       <ul>
@@ -24,7 +24,7 @@
       </ul>
     </div>
 {%- endmacro %}
-{%- macro sidebar %}
+{%- macro sidebar() %}
       {%- if builder != 'htmlhelp' %}
       <div class="sphinxsidebar">
         <div class="sphinxsidebarwrapper">
--- a/doc/book/en/Z012-create-instance.en.txt	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/Z012-create-instance.en.txt	Fri Jul 31 14:25:30 2009 +0200
@@ -8,14 +8,14 @@
 --------------------
 
 A *CubicWeb* instance is a container that
-refers to cubes and configuration parameters for your web application.
+refers to cubes and configuration parameters for your web instance.
 Each instance is stored as a directory in ``~/etc/cubicweb.d`` which enables 
-us to run your application.
+us to run your instance.
 
 What is a cube?
 ---------------
 
-Cubes represent data and basic building bricks of your web applications :
+Cubes represent data and basic building bricks of your web instances :
 blogs, person, date, addressbook and a lot more.
 
 .. XXX They related to each other by a 'Schema' which is also the PostGres representation.
@@ -35,7 +35,7 @@
 ------------------------------------
 
 We can create an instance to view our
-application in a web browser. ::
+instance in a web browser. ::
 
   cubicweb-ctl create blog myblog
 
@@ -52,17 +52,17 @@
 sufficient. You can allways modify the parameters later by editing
 configuration files. When a user/psswd is requested to access the database
 please use the login you create at the time you configured the database
-(:ref:`ConfigurationPostgres`).
+(:ref:`ConfigurationPostgresql`).
 
 It is important to distinguish here the user used to access the database and
-the user used to login to the cubicweb application. When a *CubicWeb* application
+the user used to login to the cubicweb instance. When a *CubicWeb* instance
 starts, it uses the login/psswd for the database to get the schema and handle
 low level transaction. But, when ``cubicweb-ctl create`` asks for
-a manager login/psswd of *CubicWeb*, it refers to an application user
-to administrate your web application. 
+a manager login/psswd of *CubicWeb*, it refers to an instance user
+to administrate your web instance. 
 The configuration files are stored in *~/etc/cubicweb.d/myblog/*. 
 
-To launch the web application, you just type ::
+To launch the web instance, you just type ::
 
   cubicweb-ctl start myblog
 
--- a/doc/book/en/admin/create-instance.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/create-instance.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -6,9 +6,8 @@
 Instance creation
 -----------------
 
-Now that we created our cube, we can create an instance to view our
-application in a web browser. To do so we will use a `all-in-one`
-configuration to simplify things ::
+Now that we created a cube, we can create an instance and access it via a web
+browser. We will use a `all-in-one` configuration to simplify things ::
 
   cubicweb-ctl create -c all-in-one mycube myinstance
 
@@ -21,15 +20,15 @@
 sufficient. You can anyway modify the configuration later on by editing
 configuration files. When a user/psswd is requested to access the database
 please use the login you create at the time you configured the database
-(:ref:`ConfigurationPostgres`).
+(:ref:`ConfigurationPostgresql`).
 
 It is important to distinguish here the user used to access the database and the
-user used to login to the cubicweb application. When an instance starts, it uses
+user used to login to the cubicweb instance. When an instance starts, it uses
 the login/psswd for the database to get the schema and handle low level
 transaction. But, when :command:`cubicweb-ctl create` asks for a manager
 login/psswd of *CubicWeb*, it refers to the user you will use during the
-development to administrate your web application. It will be possible, later on,
-to use this user to create others users for your final web application.
+development to administrate your web instance. It will be possible, later on,
+to use this user to create others users for your final web instance.
 
 
 Instance administration
@@ -60,5 +59,9 @@
 upgrade
 ~~~~~~~
 
+The command is::
+
+  cubicweb-ctl upgrade myinstance
+
 XXX write me
 
--- a/doc/book/en/admin/gae.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/gae.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,5 +1,7 @@
 .. -*- coding: utf-8 -*-
 
+.. _GoogleAppEngineSource:
+
 CubicWeb in Google AppEngine
 ============================
 
@@ -26,7 +28,7 @@
 
 
 Please follow instructions on how to install *CubicWeb* framework
-(:ref:`CubicWebInstallation`).
+(:ref:`SetUpEnv`).
 
 Installation
 ------------
@@ -126,14 +128,14 @@
 'cubicweb'". They disappear after the first run of i18ninstance.
 
 .. note:: The command  myapp/bin/laxctl i18ncube needs to be executed
-   only if your application is using cubes from cubicweb-apps.
+   only if your instance is using cubes from cubicweb-apps.
    Otherwise, please skip it.
 
-You will never need to add new entries in the translation catalog. Instead we would
-recommand you to use ``self.req._("msgId")`` in your application code
-to flag new message id to add to the catalog, where ``_`` refers to
-xgettext that is used to collect new strings to translate.
-While running ``laxctl i18ncube``, new string will be added to the catalogs.
+You will never need to add new entries in the translation catalog. Instead we
+would recommand you to use ``self.req._("msgId")`` in your code to flag new
+message id to add to the catalog, where ``_`` refers to xgettext that is used to
+collect new strings to translate.  While running ``laxctl i18ncube``, new string
+will be added to the catalogs.
 
 Generating the data directory
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -152,18 +154,18 @@
 
   $ python myapp/bin/laxctl genschema
 
-Application configuration
+Instance configuration
 -------------------------
 
 Authentication
 ~~~~~~~~~~~~~~
 
-You have the option of using or not google authentication for your application.
+You have the option of using or not google authentication for your instance.
 This has to be define in ``app.conf`` and ``app.yaml``.
 
 In ``app.conf`` modify the following variable::
  
-  # does this application rely on google authentication service or not.
+  # does this instance rely on google authentication service or not.
   use-google-auth=no
 
 In ``app.yaml`` comment the `login: required` set by default in the handler::
@@ -177,7 +179,7 @@
 
 
 
-Quickstart : launch the application
+Quickstart : launch the instance
 -----------------------------------
 
 On Mac OS X platforms, drag that directory on the
@@ -189,7 +191,7 @@
 
 Once the local server is started, visit `http://MYAPP_URL/_load <http://localhost:8080/_load>`_ and sign in as administrator.
 This will initialize the repository and enable you to log in into
-the application and continue the installation.
+the instance and continue the installation.
 
 You should be redirected to a page displaying a message `content initialized`.
 
--- a/doc/book/en/admin/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -7,7 +7,7 @@
 -------------------------
 
 This part is for installation and administration of the *CubicWeb* framework and
-applications based on that framework.
+instances based on that framework.
 
 .. toctree::
    :maxdepth: 1
@@ -25,11 +25,11 @@
 RQL logs
 --------
 
-You can configure the *CubicWeb* application to keep a log
+You can configure the *CubicWeb* instance to keep a log
 of the queries executed against your database. To do so,
-edit the configuration file of your application
+edit the configuration file of your instance
 ``.../etc/cubicweb.d/myapp/all-in-one.conf`` and uncomment the
 variable ``query-log-file``::
 
-  # web application query log file
+  # web instance query log file
   query-log-file=/tmp/rql-myapp.log
--- a/doc/book/en/admin/instance-config.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/instance-config.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -6,11 +6,11 @@
 
 While creating an instance, a configuration file is generated in::
 
-    $ (CW_REGISTRY) / <instance> / <configuration name>.conf
+    $ (CW_INSTANCES_DIR) / <instance> / <configuration name>.conf
 
 For example::
 
-    /etc/cubicweb.d/JPL/all-in-one.conf
+    /etc/cubicweb.d/myblog/all-in-one.conf
 
 It is a simple text file format INI. In the following description,
 each option name is prefixed with its own section and followed by its
@@ -22,7 +22,7 @@
 :`web.auth-model` [cookie]:
     authentication mode, cookie or http
 :`web.realm`:
-    realm of the application in http authentication mode
+    realm of the instance in http authentication mode
 :`web.http-session-time` [0]:
     period of inactivity of an HTTP session before it closes automatically.
     Duration in seconds, 0 meaning no expiration (or more exactly at the
@@ -70,7 +70,7 @@
     regular expression matching sites which could be "embedded" in
     the site (controllers 'embed')
 :`web.submit-url`:
-    url where the bugs encountered in the application can be mailed to
+    url where the bugs encountered in the instance can be mailed to
 
 
 RQL server configuration
@@ -92,7 +92,7 @@
 -----------------------------------
 Web server side:
 
-:`pyro-client.pyro-application-id`:
+:`pyro-client.pyro-instance-id`:
     pyro identifier of RQL server (e.g. the instance name)
 
 RQL server side:
@@ -107,7 +107,7 @@
     hostname hosting pyro server name. If no value is
     specified, it is located by a request from broadcast
 :`pyro-name-server.pyro-ns-group` [cubicweb]:
-    pyro group in which to save the application
+    pyro group in which to save the instance
 
 
 Configuring e-mail
@@ -125,9 +125,9 @@
 :`email.smtp-port [25]`:
     SMTP server port to use for outgoing mail
 :`email.sender-name`:
-    name to use for outgoing mail of the application
+    name to use for outgoing mail of the instance
 :`email.sender-addr`:
-    address for outgoing mail of the application
+    address for outgoing mail of the instance
 :`email.default dest-addrs`:
     destination addresses by default, if used by the configuration of the
     dissemination of the model (separated by commas)
--- a/doc/book/en/admin/multisources.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/multisources.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,6 @@
-Integrating some data from another instance
-===========================================
+Multiple sources of data
+========================
 
-XXX feed me
\ No newline at end of file
+Data sources include SQL, LDAP, RQL, mercurial and subversion.
+
+XXX feed me
--- a/doc/book/en/admin/setup.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/setup.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -39,7 +39,7 @@
   apt-get install cubicweb cubicweb-dev
 
 `cubicweb` installs the framework itself, allowing you to create
-new applications.
+new instances.
 
 `cubicweb-dev` installs the development environment allowing you to
 develop new cubes.
@@ -69,22 +69,29 @@
   hg fclone http://www.logilab.org/hg/forests/cubicweb
 
 See :ref:`MercurialPresentation` for more details about Mercurial.
+When cloning a repository, you might be set in a development branch
+(the 'default' branch). You should check that the branches of the
+repositories are set to 'stable' (using `hg up stable` for each one)
+if you do not intend to develop the framework itself.
 
 In both cases, make sure you have installed the dependencies (see appendixes for
 the list).
 
-Postgres installation
-`````````````````````
+PostgreSQL installation
+```````````````````````
 
-Please refer to the `Postgresql project online documentation`_.
+Please refer to the `PostgreSQL project online documentation`_.
 
-.. _`Postgresql project online documentation`: http://www.postgresql.org/
+.. _`PostgreSQL project online documentation`: http://www.postgresql.org/
 
-You need to install the three following packages: `postgres-8.3`,
-`postgres-contrib-8.3` and `postgresql-plpython-8.3`.
+You need to install the three following packages: `postgresql-8.3`,
+`postgresql-contrib-8.3` and `postgresql-plpython-8.3`.
 
 
-Then you can install:
+Other dependencies
+``````````````````
+
+You can also install:
 
 * `pyro` if you wish the repository to be accessible through Pyro
   or if the client and the server are not running on the same machine
@@ -105,19 +112,20 @@
 Add the following lines to either `.bashrc` or `.bash_profile` to configure
 your development environment ::
 
-  export PYTHONPATH=/full/path/to/cubicweb-forest
+    export PYTHONPATH=/full/path/to/cubicweb-forest
 
-If you installed the debian packages, no configuration is required.
-Your new cubes will be placed in `/usr/share/cubicweb/cubes` and
-your applications will be placed in `/etc/cubicweb.d`.
+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`.
 
-To use others directories then you will have to configure the
-following environment variables as follows::
+You may run a system-wide install of *CubicWeb* in "user mode" and use it for
+development by setting the following environment variable::
 
+    export CW_MODE=user
     export CW_CUBES_PATH=~/lib/cubes
-    export CW_REGISTRY=~/etc/cubicweb.d/
-    export CW_INSTANCE_DATA=$CW_REGISTRY
-    export CW_RUNTIME=/tmp
+    export CW_INSTANCES_DIR=~/etc/cubicweb.d/
+    export CW_INSTANCES_DATA_DIR=$CW_INSTANCES_DIR
+    export CW_RUNTIME_DIR=/tmp
 
 .. note::
     The values given above are our suggestions but of course
@@ -129,22 +137,22 @@
 
 
 
-.. _ConfigurationPostgres:
+.. _ConfigurationPostgresql:
 
-Postgres configuration
-``````````````````````
+PostgreSQL configuration
+````````````````````````
 
 .. note::
-    If you already have an existing cluster and postgres server
+    If you already have an existing cluster and PostgreSQL server
     running, you do not need to execute the initilization step
-    of your Postgres database.
+    of your PostgreSQL database.
 
-* First, initialize the database Postgres with the command ``initdb``.
+* First, initialize the database PostgreSQL with the command ``initdb``.
   ::
 
     $ initdb -D /path/to/pgsql
 
-  Once initialized, start the database server Postgres
+  Once initialized, start the database server PostgreSQL
   with the command::
 
     $ postgres -D /path/to/psql
@@ -176,7 +184,7 @@
 
   This login/password will be requested when you will create an
   instance with `cubicweb-ctl create` to initialize the database of
-  your application.
+  your instance.
 
 .. note::
     The authentication method can be configured in ``pg_hba.conf``.
@@ -194,13 +202,17 @@
 
 MySql configuration
 ```````````````````
-Yout must add the following lines in /etc/mysql/my.cnf file::
+Yout must add the following lines in ``/etc/mysql/my.cnf`` file::
 
     transaction-isolation = READ-COMMITTED
     default-storage-engine=INNODB
     default-character-set=utf8
     max_allowed_packet = 128M
 
+.. note::
+    It is unclear whether mysql supports indexed string of arbitrary lenght or
+    not.
+
 Pyro configuration
 ------------------
 
--- a/doc/book/en/admin/site-config.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/admin/site-config.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,7 @@
 
 .. image:: ../images/lax-book.03-site-config-panel.en.png
 
-This panel allows you to configure the appearance of your application site.
+This panel allows you to configure the appearance of your instance site.
 Six menus are available and we will go through each of them to explain how
 to use them.
 
@@ -65,9 +65,9 @@
 
 Boxes
 ~~~~~
-The application has already a pre-defined set of boxes you can use right away.
+The instance has already a pre-defined set of boxes you can use right away.
 This configuration section allows you to place those boxes where you want in the
-application interface to customize it.
+instance interface to customize it.
 
 The available boxes are :
 
@@ -82,7 +82,7 @@
 * search box : search box
 
 * startup views box : box listing the configuration options available for
-  the application site, such as `Preferences` and `Site Configuration`
+  the instance site, such as `Preferences` and `Site Configuration`
 
 Components
 ~~~~~~~~~~
--- a/doc/book/en/annexes/cookbook.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/cookbook.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -9,8 +9,10 @@
 
 * How to import LDAP users in *CubicWeb*?
 
+  [XXX distribute this script with cubicweb instead]
+
   Here is a very useful script which enables you to import LDAP users
-  into your *CubicWeb* application by running the following: ::
+  into your *CubicWeb* instance by running the following: ::
 
 
     import os
@@ -66,7 +68,7 @@
 * How to load data from a script?
 
   The following script aims at loading data within a script assuming pyro-nsd is
-  running and your application is configured with ``pyro-server=yes``, otherwise
+  running and your instance is configured with ``pyro-server=yes``, otherwise
   you would not be able to use dbapi. ::
 
     from cubicweb import dbapi
--- a/doc/book/en/annexes/cubicweb-ctl.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/cubicweb-ctl.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -26,8 +26,8 @@
 ------------------------
 
 * ``newcube``, create a new cube on the file system based on the name
-  given in the parameters. This command create a cube from an application
-  skeleton that includes default files required for debian packaging.
+  given in the parameters. This command create a cube from a skeleton
+  that includes default files required for debian packaging.
 
 
 Command to create an instance
@@ -69,7 +69,7 @@
 * ``db-check``, checks data integrity of an instance. If the automatic correction
   is activated, it is recommanded to create a dump before this operation.
 * ``schema-sync``, synchronizes the persistent schema of an instance with
-  the application schema. It is recommanded to create a dump before this operation.
+  the instance schema. It is recommanded to create a dump before this operation.
 
 Commands to maintain i18n catalogs
 ----------------------------------
--- a/doc/book/en/annexes/depends.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/depends.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -44,5 +44,8 @@
 * indexer - http://www.logilab.org/project/indexer -
   http://pypi.python.org/pypi/indexer - included in the forest
 
+* fyzz - http://www.logilab.org/project/fyzz - http://pypi.python.org/pypi/fyzz
+  - included in the forest
+
 Any help with the packaging of CubicWeb for more than Debian/Ubuntu (including
 eggs, buildouts, etc) will be greatly appreciated.
--- a/doc/book/en/annexes/faq.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/faq.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -14,80 +14,79 @@
 Why does not CubicWeb have a template language ?
 ------------------------------------------------
 
-  There are enough template languages out there. You can use your
-  preferred template language if you want. [explain how to use a
-  template language]
+There are enough template languages out there. You can use your
+preferred template language if you want. [explain how to use a
+template language]
 
-  *CubicWeb* does not define its own templating language as this was
-  not our goal. Based on our experience, we realized that
-  we could gain productivity by letting designers use design tools
-  and developpers develop without the use of the templating language
-  as an intermediary that could not be anyway efficient for both parties.
-  Python is the templating language that we use in *CubicWeb*, but again,
-  it does not prevent you from using a templating language.
+*CubicWeb* does not define its own templating language as this was
+not our goal. Based on our experience, we realized that
+we could gain productivity by letting designers use design tools
+and developpers develop without the use of the templating language
+as an intermediary that could not be anyway efficient for both parties.
+Python is the templating language that we use in *CubicWeb*, but again,
+it does not prevent you from using a templating language.
 
-  The reason template languages are not used in this book is that
-  experience has proved us that using pure python was less cumbersome.
+The reason template languages are not used in this book is that
+experience has proved us that using pure python was less cumbersome.
 
 Why do you think using pure python is better than using a template language ?
 -----------------------------------------------------------------------------
 
-  Python is an Object Oriented Programming language and as such it
-  already provides a consistent and strong architecture and syntax
-  a templating language would not reach.
+Python is an Object Oriented Programming language and as such it
+already provides a consistent and strong architecture and syntax
+a templating language would not reach.
 
-  When doing development, you need a real language and template
-  languages are not real languages.
+When doing development, you need a real language and template
+languages are not real languages.
 
-  Using Python enables developing applications for which code is
-  easier to maintain with real functions/classes/contexts
-  without the need of learning a new dialect. By using Python,
-  we use standard OOP techniques and this is a key factor in a
-  robust application.
+Using Python instead of a template langage for describing the user interface
+makes it to maintain with real functions/classes/contexts without the need of
+learning a new dialect. By using Python, we use standard OOP techniques and
+this is a key factor in a robust application.
 
 Why do you use the LGPL license to prevent me from doing X ?
 ------------------------------------------------------------
 
-  LGPL means that *if* you redistribute your application, you need to
-  redistribute the changes you made to CubicWeb under the LGPL licence.
+LGPL means that *if* you redistribute your application, you need to
+redistribute the changes you made to CubicWeb under the LGPL licence.
 
-  Publishing a web site has nothing to do with redistributing
-  source code. A fair amount of companies use modified LGPL code
-  for internal use. And someone could publish a *CubicWeb* component
-  under a BSD licence for others to plug into a LGPL framework without
-  any problem. The only thing we are trying to prevent here is someone
-  taking the framework and packaging it as closed source to his own
-  clients.
+Publishing a web site has nothing to do with redistributing
+source code. A fair amount of companies use modified LGPL code
+for internal use. And someone could publish a *CubicWeb* component
+under a BSD licence for others to plug into a LGPL framework without
+any problem. The only thing we are trying to prevent here is someone
+taking the framework and packaging it as closed source to his own
+clients.
 
 
 CubicWeb looks pretty recent. Is it stable ?
 --------------------------------------------
 
-  It is constantly evolving, piece by piece.  The framework has evolved since
-  2001 and data has been migrated from one schema to the other ever since. There
-  is a well-defined way to handle data and schema migration.
+It is constantly evolving, piece by piece.  The framework has evolved since
+2001 and data has been migrated from one schema to the other ever since. There
+is a well-defined way to handle data and schema migration.
 
 Why is the RQL query language looking similar to X ?
 -----------------------------------------------------
 
-  It may remind you of SQL but it is higher level than SQL, more like
-  SPARQL. Except that SPARQL did not exist when we started the project.
-  Having SPARQL as a query language has been in our backlog for years.
+It may remind you of SQL but it is higher level than SQL, more like
+SPARQL. Except that SPARQL did not exist when we started the project.
+With version 3.4, CubicWeb has support for SPARQL.
 
-  That RQL language is what is going to make a difference with django-
-  like frameworks for several reasons.
+That RQL language is what is going to make a difference with django-
+like frameworks for several reasons.
 
-  1. accessing data is *much* easier with it. One can write complex
-     queries with RQL that would be tedious to define and hard to maintain
-     using an object/filter suite of method calls.
+1. accessing data is *much* easier with it. One can write complex
+   queries with RQL that would be tedious to define and hard to maintain
+   using an object/filter suite of method calls.
 
-  2. it offers an abstraction layer allowing your applications to run
-     on multiple back-ends. That means not only various SQL backends
-     (postgresql, sqlite, mysql), but also multiple databases at the
-     same time, and also non-SQL data stores like LDAP directories and
-     subversion/mercurial repositories (see the `vcsfile`
-     component). Google App Engine is yet another supported target for
-     RQL.
+2. it offers an abstraction layer allowing your applications to run
+   on multiple back-ends. That means not only various SQL backends
+   (postgresql, sqlite, mysql), but also multiple databases at the
+   same time, and also non-SQL data stores like LDAP directories and
+   subversion/mercurial repositories (see the `vcsfile`
+   component). Google App Engine is yet another supported target for
+   RQL.
 
 [copy answer from forum, explain why similar to sparql and why better
   than django and SQL]
@@ -101,7 +100,9 @@
 How is security implemented ?
 ------------------------------
 
-  This is an example of how it works in our framework::
+This is an example of how it works in our framework:
+
+.. sourcecode:: python
 
     class Version(EntityType):
         """a version is defining the content of a particular project's
@@ -111,16 +112,17 @@
                        'update': ('managers', 'logilab', 'owners',),
                        'delete': ('managers', ),
                        'add': ('managers', 'logilab',
-                           ERQLExpression('X version_of PROJ, U in_group G, '
-                                          'PROJ require_permission P, '
-                                          'P name "add_version", P require_group G'),)}
+                               ERQLExpression('X version_of PROJ, U in_group G, '
+                                              'PROJ require_permission P, '
+                                              'P name "add_version", P require_group G'),)}
 
-  The above means that permission to read a Version is granted to any
-  user that is part of one of the groups 'managers', 'users', 'guests'.
-  The 'add' permission is granted to users in group 'managers' or
-  'logilab' and to users in group G, if G is linked by a permission
-  entity named "add_version" to the version's project.
-  ::
+The above means that permission to read a Version is granted to any
+user that is part of one of the groups 'managers', 'users', 'guests'.
+The 'add' permission is granted to users in group 'managers' or
+'logilab' and to users in group G, if G is linked by a permission
+entity named "add_version" to the version's project.
+
+.. sourcecode:: python
 
     class version_of(RelationType):
         """link a version to its project. A version is necessarily linked
@@ -129,20 +131,20 @@
         permissions = {'read': ('managers', 'users', 'guests',),
                        'delete': ('managers', ),
                        'add': ('managers', 'logilab',
-                            RRQLExpression('O require_permission P, P name "add_version",
-                            'U in_group G, P require_group G'),) }
+                               RRQLExpression('O require_permission P, P name "add_version", '
+                               'U in_group G, P require_group G'),) }
 
-  You can find additional information in the section :ref:`security`.
+You can find additional information in the section :ref:`security`.
 
-  [XXX what does the second example means in addition to the first one?]
+[XXX what does the second example means in addition to the first one?]
 
 
 What is `Error while publishing rest text ...` ?
 ------------------------------------------------
 
-  While modifying the description of an entity, you get an error message in
-  the application `Error while publishing ...` for Rest text and plain text.
-  The server returns a traceback like as follows ::
+While modifying the description of an entity, you get an error message in
+the instance `Error while publishing ...` for Rest text and plain text.
+The server returns a traceback like as follows ::
 
       2008-10-06 15:05:08 - (cubicweb.rest) ERROR: error while publishing ReST text
       Traceback (most recent call last):
@@ -151,73 +153,69 @@
       file = __builtin__.open(filename, mode, buffering)
       TypeError: __init__() takes at most 3 arguments (4 given)
 
-
-  This can be fixed by applying the patch described in :
-  http://code.google.com/p/googleappengine/issues/detail?id=48
+This can be fixed by applying the patch described in :
+http://code.google.com/p/googleappengine/issues/detail?id=48
 
 What are hooks used for ?
 -------------------------
 
-  Hooks are executed around (actually before or after) events.  The
-  most common events are data creation, update and deletion.  They
-  permit additional constraint checking (those not expressible at the
-  schema level), pre and post computations depending on data
-  movements.
+Hooks are executed around (actually before or after) events.  The
+most common events are data creation, update and deletion.  They
+permit additional constraint checking (those not expressible at the
+schema level), pre and post computations depending on data
+movements.
 
-  As such, they are a vital part of the framework.
+As such, they are a vital part of the framework.
 
-  Other kinds of hooks, called Operations, are available
-  for execution just before commit.
+Other kinds of hooks, called Operations, are available
+for execution just before commit.
 
 When should you define an HTML template rather than define a graphical component ?
 ----------------------------------------------------------------------------------
 
-  An HTML template cannot contain code, hence it is only about static
-  content.  A component is made of code and operations that apply on a
-  well defined context (request, result set). It enables much more
-  dynamic views.
+An HTML template cannot contain code, hence it is only about static
+content.  A component is made of code and operations that apply on a
+well defined context (request, result set). It enables much more
+dynamic views.
 
 What is the difference between `AppRsetObject` and `AppObject` ?
 ----------------------------------------------------------------
 
-  `AppRsetObject` instances are selected on a request and a result
-  set. `AppObject` instances are directly selected by id.
+`AppRsetObject` instances are selected on a request and a result
+set. `AppObject` instances are directly selected by id.
 
 How to update a database after a schema modification ?
 ------------------------------------------------------
 
-  It depends on what has been modified in the schema.
-
-  * Update of an attribute permissions and properties:
-    ``synchronize_eschema('MyEntity')``.
+It depends on what has been modified in the schema.
 
-  * Update of a relation permissions and properties:
-    ``synchronize_rschema('MyRelation')``.
+* Update the permissions and properties of an entity or a relation:
+  ``sync_schema_props_perms('MyEntityOrRelation')``.
 
-  * Add an attribute: ``add_attribute('MyEntityType', 'myattr')``.
+* Add an attribute: ``add_attribute('MyEntityType', 'myattr')``.
 
-  * Add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``.
+* Add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``.
 
 
 How to create an anonymous user ?
 ---------------------------------
 
-  This allows to bypass authentication for your site. In the
-  ``all-in-one.conf`` file of your instance, define the anonymous user
-  as follows ::
+This allows to bypass authentication for your site. In the
+``all-in-one.conf`` file of your instance, define the anonymous user
+as follows ::
 
-    # login of the CubicWeb user account to use for anonymous user (if you want to
-    # allow anonymous)
-    anonymous-user=anon
+  # login of the CubicWeb user account to use for anonymous user (if you want to
+  # allow anonymous)
+  anonymous-user=anon
 
-    # password of the CubicWeb user account matching login
-    anonymous-password=anon
+  # password of the CubicWeb user account matching login
+  anonymous-password=anon
 
-  You also must ensure that this `anon` user is a registered user of
-  the DB backend. If not, you can create through the administation
-  interface of your instance by adding a user with the role `guests`.
-  This could be the admin account (for development
-  purposes, of course).
+You also must ensure that this `anon` user is a registered user of
+the DB backend. If not, you can create through the administation
+interface of your instance by adding a user with the role `guests`.
+This could be the admin account (for development
+purposes, of course).
 
 .. note::
     While creating a new instance, you can decide to allow access
@@ -225,58 +223,60 @@
     decribed above.
 
 
-How to change the application logo ?
+How to change the instance logo ?
 ------------------------------------
 
-  There are two ways of changing the logo.
+There are two ways of changing the logo.
 
-  1. The easiest way to use a different logo is to replace the existing
-     ``logo.png`` in ``myapp/data`` by your prefered icon and refresh.
-     By default all application will look for a ``logo.png`` to be
-     rendered in the logo section.
+1. The easiest way to use a different logo is to replace the existing
+   ``logo.png`` in ``myapp/data`` by your prefered icon and refresh.
+   By default all instance will look for a ``logo.png`` to be
+   rendered in the logo section.
 
-     .. image:: ../images/lax-book.06-main-template-logo.en.png
+   .. image:: ../images/lax-book.06-main-template-logo.en.png
 
-  2. In your cube directory, you can specify which file to use for the logo.
-     This is configurable in ``mycube/data/external_resources``: ::
+2. In your cube directory, you can specify which file to use for the logo.
+   This is configurable in ``mycube/data/external_resources``: ::
 
-       LOGO = DATADIR/path/to/mylogo.gif
+     LOGO = DATADIR/path/to/mylogo.gif
 
-     where DATADIR is ``mycube/data``.
+   where DATADIR is ``mycube/data``.
 
 
 How to configure a LDAP source ?
 --------------------------------
 
-  Your instance's sources are defined in ``/etc/cubicweb.d/myapp/sources``.
-  Configuring an LDAP source is about declaring that source in your
-  instance configuration file such as: ::
+Your instance's sources are defined in ``/etc/cubicweb.d/myapp/sources``.
+Configuring an LDAP source is about declaring that source in your
+instance configuration file such as: ::
 
-    [ldapuser]
-    adapter=ldapuser
-    # ldap host
-    host=myhost
-    # base DN to lookup for usres
-    user-base-dn=ou=People,dc=mydomain,dc=fr
-    # user search scope
-    user-scope=ONELEVEL
-    # classes of user
-    user-classes=top,posixAccount
-    # attribute used as login on authentication
-    user-login-attr=uid
-    # name of a group in which ldap users will be by default
-    user-default-group=users
-    # map from ldap user attributes to cubicweb attributes
-    user-attrs-map=gecos:email,uid:login
+  [ldapuser]
+  adapter=ldapuser
+  # ldap host
+  host=myhost
+  # base DN to lookup for usres
+  user-base-dn=ou=People,dc=mydomain,dc=fr
+  # user search scope
+  user-scope=ONELEVEL
+  # classes of user
+  user-classes=top,posixAccount
+  # attribute used as login on authentication
+  user-login-attr=uid
+  # name of a group in which ldap users will be by default
+  user-default-group=users
+  # map from ldap user attributes to cubicweb attributes
+  user-attrs-map=gecos:email,uid:login
 
-  Any change applied to configuration file requires to restart your
-  application.
+Any change applied to configuration file requires to restart your
+instance.
 
 I get NoSelectableObject exceptions, how do I debug selectors ?
 ---------------------------------------------------------------
 
-  You just need to put the appropriate context manager around view/component
-  selection (one standard place in in vreg.py) : ::
+You just need to put the appropriate context manager around view/component
+selection (one standard place in in vreg.py):
+
+.. sourcecode:: python
 
     def possible_objects(self, registry, *args, **kwargs):
         """return an iterator on possible objects in a registry for this result set
@@ -291,40 +291,43 @@
                 except NoSelectableObject:
                     continue
 
-  Don't forget the 'from __future__ improt with_statement' at the
-  module top-level.
+Don't forget the 'from __future__ import with_statement' at the module
+top-level.
 
-  This will yield additional WARNINGs, like this:
-  ::
+This will yield additional WARNINGs, like this::
 
     2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
 
 How to format an entity date attribute ?
 ----------------------------------------
 
-  If your schema has an attribute of type Date or Datetime, you might
-  want to format it. First, you should define your preferred format using
-  the site configuration panel ``http://appurl/view?vid=systempropertiesform``
-  and then set ``ui.date`` and/or ``ui.datetime``.
-  Then in the view code, use::
+If your schema has an attribute of type Date or Datetime, you might
+want to format it. First, you should define your preferred format using
+the site configuration panel ``http://appurl/view?vid=systempropertiesform``
+and then set ``ui.date`` and/or ``ui.datetime``.
+Then in the view code, use:
+
+.. sourcecode:: python
 
     self.format_date(entity.date_attribute)
 
 Can PostgreSQL and CubicWeb authentication work with kerberos ?
 ----------------------------------------------------------------
 
-  If you have PostgreSQL set up to accept kerberos authentication, you can set
-  the db-host, db-name and db-user parameters in the `sources` configuration
-  file while leaving the password blank. It should be enough for your
-  application to connect to postgresql with a kerberos ticket.
+If you have PostgreSQL set up to accept kerberos authentication, you can set
+the db-host, db-name and db-user parameters in the `sources` configuration
+file while leaving the password blank. It should be enough for your
+instance to connect to postgresql with a kerberos ticket.
 
 
 How to load data from a script ?
 --------------------------------
 
-  The following script aims at loading data within a script assuming pyro-nsd is
-  running and your application is configured with ``pyro-server=yes``, otherwise
-  you would not be able to use dbapi. ::
+The following script aims at loading data within a script assuming pyro-nsd is
+running and your instance is configured with ``pyro-server=yes``, otherwise
+you would not be able to use dbapi.
+
+.. sourcecode:: python
 
     from cubicweb import dbapi
 
@@ -337,24 +340,30 @@
 What is the CubicWeb datatype corresponding to GAE datastore's UserProperty ?
 -----------------------------------------------------------------------------
 
-  If you take a look at your application schema and
-  click on "display detailed view of metadata" you will see that there
-  is a Euser entity in there. That's the one that is modeling users. The
-  thing that corresponds to a UserProperty is a relationship between
-  your entity and the Euser entity. As in ::
+If you take a look at your instance schema and
+click on "display detailed view of metadata" you will see that there
+is a Euser entity in there. That's the one that is modeling users. The
+thing that corresponds to a UserProperty is a relationship between
+your entity and the Euser entity. As in:
+
+.. sourcecode:: python
 
     class TodoItem(EntityType):
        text = String()
        todo_by = SubjectRelation('Euser')
 
-  [XXX check that cw handle users better by
-  mapping Google Accounts to local Euser entities automatically]
+[XXX check that cw handle users better by mapping Google Accounts to local Euser
+entities automatically]
 
 
 How to reset the password for user joe ?
 ----------------------------------------
 
-  You need to generate a new encrypted password::
+If you want to reset the admin password for ``myinstance``, do::
+
+    $ cubicweb-ctl reset-admin-pwd myinstance
+
+You need to generate a new encrypted password::
 
     $ python
     >>> from cubicweb.server.utils import crypt_password
@@ -362,8 +371,17 @@
     'qHO8282QN5Utg'
     >>>
 
-  and paste it in the database::
+and paste it in the database::
 
     $ psql mydb
     mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe';
     UPDATE 1
+
+I've just created a user in a group and it doesn't work !
+---------------------------------------------------------
+
+You are probably getting errors such as ::
+
+  remove {'PR': 'Project', 'C': 'CWUser'} from solutions since your_user has no read access to cost
+
+This is because you have to put your user in the "users" group. The user has to be in both groups.
--- a/doc/book/en/annexes/rql/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/rql/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -8,5 +8,4 @@
 
    intro
    language
-   dbapi
    implementation
--- a/doc/book/en/annexes/rql/intro.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/annexes/rql/intro.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -5,41 +5,38 @@
 Goals of RQL
 ~~~~~~~~~~~~
 
-The goal is to have a language emphasizing the way of browsing
-relations. As such, attributes will be regarded as cases of
-special relations (in terms of implementation, the language
-user should see virtually no difference between an attribute and a
+The goal is to have a language emphasizing the way of browsing relations. As
+such, attributes will be regarded as cases of special relations (in terms of
+implementation, the user should see no difference between an attribute and a
 relation).
 
-RQL is inspired by SQL but is the highest level. A knowledge of the
-*CubicWeb* schema defining the application is necessary.
-
 Comparison with existing languages
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 SQL
 ```
-RQL builds on the features of SQL but is at a higher level
-(the current implementation of RQL generates SQL). For that it is limited
-to the way of browsing relations and introduces variables.
-The user does not need to know the model underlying SQL, but the *CubicWeb*
-schema defining the application.
+
+RQL may remind of SQL but works at a higher abstraction level (the *CubicWeb*
+framework generates SQL from RQL to fetch data from relation databases). RQL is
+focused on browsing relations. The user needs only to know about the *CubicWeb*
+data model he is querying, but not about the underlying SQL model.
+
+Sparql
+``````
+
+The query language most similar to RQL is SPARQL_, defined by the W3C to serve
+for the semantic web.
 
 Versa
 `````
-We should look in more detail, but here are already some ideas for
-the moment ... Versa_ is the language most similar to what we wanted
-to do, but the model underlying data being RDF, there is some
-number of things such as namespaces or handling of the RDF types which
-does not interest us. On the functionality level, Versa_ is very comprehensive
-including through many functions of conversion and basic types manipulation,
-which may need to be guided at one time or another.
-Finally, the syntax is a little esoteric.
 
-Sparql
-``````
-The query language most similar to RQL is SPARQL_, defined by the W3C to serve
-for the semantic web.
+We should look in more detail, but here are already some ideas for the moment
+... Versa_ is the language most similar to what we wanted to do, but the model
+underlying data being RDF, there is some number of things such as namespaces or
+handling of the RDF types which does not interest us. On the functionality
+level, Versa_ is very comprehensive including through many functions of
+conversion and basic types manipulation, which may need to be guided at one time
+or another.  Finally, the syntax is a little esoteric.
 
 
 The different types of queries
--- a/doc/book/en/conf.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/conf.py	Fri Jul 31 14:25:30 2009 +0200
@@ -51,7 +51,7 @@
 # The short X.Y version.
 version = '0.54'
 # The full version, including alpha/beta/rc tags.
-release = '3.2'
+release = '3.4'
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
--- a/doc/book/en/development/cubes/available-cubes.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/cubes/available-cubes.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -2,7 +2,7 @@
 Available cubes
 ---------------
 
-An application is based on several basic cubes. In the set of available
+An instance is based on several basic cubes. In the set of available
 basic cubes we can find for example :
 
 Base entity types
--- a/doc/book/en/development/cubes/layout.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/cubes/layout.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -80,7 +80,7 @@
 * ``entities`` contains the entities definition (server side and web interface)
 * ``sobjects`` contains hooks and/or views notifications (server side only)
 * ``views`` contains the web interface components (web interface only)
-* ``test`` contains tests related to the application (not installed)
+* ``test`` contains tests related to the cube (not installed)
 * ``i18n`` contains message catalogs for supported languages (server side and
   web interface)
 * ``data`` contains data files for static content (images, css, javascripts)
--- a/doc/book/en/development/datamodel/baseschema.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/datamodel/baseschema.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,9 +1,9 @@
 
-Pre-defined schemas in the library
-----------------------------------
+Pre-defined entities in the library
+-----------------------------------
 
 The library defines a set of entity schemas that are required by the system
-or commonly used in *CubicWeb* applications.
+or commonly used in *CubicWeb* instances.
 
 
 Entity types used to store the schema
@@ -18,7 +18,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * `CWUser`, system users
 * `CWGroup`, users groups
-* `CWPermission`, used to configure the security of the application
+* `CWPermission`, used to configure the security of the instance
 
 Entity types used to manage workflows
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -28,11 +28,11 @@
 
 Other entity types
 ~~~~~~~~~~~~~~~~~~
-* `CWCache`
-* `CWProperty`, used to configure the application
+* `CWCache`, cache entities used to improve performances
+* `CWProperty`, used to configure the instance
 
 * `EmailAddress`, email address, used by the system to send notifications
   to the users and also used by others optionnals schemas
 
 * `Bookmark`, an entity type used to allow a user to customize his links within
-  the application
+  the instance
--- a/doc/book/en/development/datamodel/define-workflows.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/datamodel/define-workflows.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -2,8 +2,8 @@
 
 .. _Workflow:
 
-An Example: Workflow definition
-===============================
+Define a Workflow
+=================
 
 General
 -------
@@ -20,7 +20,7 @@
 -----------------
 
 We want to create a workflow to control the quality of the BlogEntry
-submitted on your application. When a BlogEntry is created by a user
+submitted on your instance. When a BlogEntry is created by a user
 its state should be `submitted`. To be visible to all, it has to
 be in the state `published`. To move it from `submitted` to `published`,
 we need a transition that we can call `approve_blogentry`.
--- a/doc/book/en/development/datamodel/definition.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/datamodel/definition.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -3,7 +3,7 @@
 Yams *schema*
 -------------
 
-The **schema** is the core piece of a *CubicWeb* application as it defines
+The **schema** is the core piece of a *CubicWeb* instance as it defines
 the handled data model. It is based on entity types that are either already
 defined in the *CubicWeb* standard library; or more specific types, that
 *CubicWeb* expects to find in one or more Python files under the directory
@@ -24,9 +24,8 @@
 They are implicitely imported (as well as the special the function "_"
 for translation :ref:`internationalization`).
 
-The instance schema of an application is defined on all appobjects by a .schema
-class attribute set on registration.  It's an instance of
-:class:`yams.schema.Schema`.
+The instance schema is defined on all appobjects by a .schema class attribute set
+on registration.  It's an instance of :class:`yams.schema.Schema`.
 
 Entity type
 ~~~~~~~~~~~
--- a/doc/book/en/development/datamodel/inheritance.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/datamodel/inheritance.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -2,4 +2,7 @@
 Inheritance
 -----------
 
+When describing a data model, entities can inherit from other entities as is
+common in object-oriented programming.
+
 XXX WRITME
--- a/doc/book/en/development/devcore/appobject.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devcore/appobject.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -19,9 +19,9 @@
 At the recording, the following attributes are dynamically added to
 the *subclasses*:
 
-* `vreg`, the `vregistry` of the application
-* `schema`, the application schema
-* `config`, the application configuration
+* `vreg`, the `vregistry` of the instance
+* `schema`, the instance schema
+* `config`, the instance configuration
 
 We also find on instances, the following attributes:
 
@@ -36,7 +36,7 @@
     can be specified through the special parameter `method` (the connection
     is theoretically done automatically :).
 
-  * `datadir_url()`, returns the directory of the application data
+  * `datadir_url()`, returns the directory of the instance data
     (contains static files such as images, css, js...)
 
   * `base_url()`, shortcut to `req.base_url()`
@@ -57,9 +57,9 @@
 
 :Data formatting:
   * `format_date(date, date_format=None, time=False)` returns a string for a
-    mx date time according to application's configuration
+    mx date time according to instance's configuration
   * `format_time(time)` returns a string for a mx date time according to
-    application's configuration
+    instance's configuration
 
 :And more...:
 
--- a/doc/book/en/development/devcore/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devcore/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,6 @@
    :maxdepth: 1
 
    vreg.rst
-   selection.rst
    appobject.rst
    selectors.rst
    dbapi.rst
--- a/doc/book/en/development/devcore/vreg.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devcore/vreg.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -27,7 +27,7 @@
 
 Examples:
 
-.. code-block:: python
+.. sourcecode:: python
 
    # web/views/basecomponents.py
    def registration_callback(vreg):
@@ -45,7 +45,7 @@
 API d'enregistrement des objets
 ```````````````````````````````
 
-.. code-block:: python
+.. sourcecode:: python
 
    register(obj, registryname=None, oid=None, clear=False)
 
--- a/doc/book/en/development/devrepo/sessions.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devrepo/sessions.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -5,21 +5,22 @@
 
 There are three kinds of sessions.
 
-* user sessions are the most common: they are related to users and
+* `user sessions` are the most common: they are related to users and
   carry security checks coming with user credentials
 
-* super sessions are children of ordinary user sessions and allow to
+* `super sessions` are children of ordinary user sessions and allow to
   bypass security checks (they are created by calling unsafe_execute
   on a user session); this is often convenient in hooks which may
   touch data that is not directly updatable by users
 
-* internal sessions have all the powers; they are also used in only a
+* `internal sessions` have all the powers; they are also used in only a
   few situations where you don't already have an adequate session at
   hand, like: user authentication, data synchronisation in
   multi-source contexts
 
-Do not confuse the session type with their connection mode, for
-instance : 'in memory' or 'pyro'.
+.. note::
+  Do not confuse the session type with their connection mode, for
+  instance : 'in memory' or 'pyro'.
 
 [WRITE ME]
 
--- a/doc/book/en/development/devweb/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devweb/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,7 +1,7 @@
 Web development
 ===============
 
-In this chapter, we will core api for web development in the CubicWeb framework.
+In this chapter, we will describe the core api for web development in the *CubicWeb* framework.
 
 .. toctree::
    :maxdepth: 1
--- a/doc/book/en/development/devweb/internationalization.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devweb/internationalization.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,12 +1,12 @@
 .. -*- coding: utf-8 -*-
 
-.. _internationalisation:
+.. _internationalization:
 
 
-Internationalisation
+Internationalization
 ---------------------
 
-Cubicweb fully supports the internalization of it's content and interface.
+Cubicweb fully supports the internalization of its content and interface.
 
 Cubicweb's interface internationalization is based on the translation project `GNU gettext`_.
 
@@ -16,7 +16,7 @@
 
 * in your Python code and cubicweb-tal templates : mark translatable strings
 
-* in your application : handle the translation catalog
+* in your instance : handle the translation catalog
 
 String internationalization
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -66,16 +66,18 @@
 .. note::
 
    We dont need to mark the translation strings of entities/relations
-   used by a particular application's schema as they are generated
+   used by a particular instance's schema as they are generated
    automatically.
 
+If you need to add messages on top of those that can be found in the source,
+you can create a file named `i18n/static-messages.pot`.
 
 Handle the translation catalog
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Once the internationalization is done in your application's code, you need
-to populate and update the translation catalog. Cubicweb provides the
-following commands for this purpose:
+Once the internationalization is done in your code, you need to populate and
+update the translation catalog. Cubicweb provides the following commands for this
+purpose:
 
 
 * `i18ncubicweb` updates Cubicweb framework's translation
@@ -83,28 +85,28 @@
   need to use this command.
 
 * `i18ncube` updates the translation catalogs of *one particular
-  component* (or of all components). After this command is
+  cube* (or of all cubes). After this command is
   executed you must update the translation files *.po* in the "i18n"
   directory of your template. This command will of course not remove
   existing translations still in use.
 
 * `i18ninstance` recompile the translation catalogs of *one particular
   instance* (or of all instances) after the translation catalogs of
-  its components have been updated. This command is automatically
+  its cubes have been updated. This command is automatically
   called every time you create or update your instance. The compiled
   catalogs (*.mo*) are stored in the i18n/<lang>/LC_MESSAGES of
-  application where `lang` is the language identifier ('en' or 'fr'
+  instance where `lang` is the language identifier ('en' or 'fr'
   for exemple).
 
 
 Example
 ```````
-You have added and/or modified some translation strings in your application
-(after creating a new view or modifying the application's schema for exemple).
+You have added and/or modified some translation strings in your cube
+(after creating a new view or modifying the cube's schema for exemple).
 To update the translation catalogs you need to do:
 
-1. `cubicweb-ctl i18ncube <component>`
-2. Edit the <component>/xxx.po  files and add missing translations (empty `msgstr`)
+1. `cubicweb-ctl i18ncube <cube>`
+2. Edit the <cube>/i18n/xxx.po  files and add missing translations (empty `msgstr`)
 3. `hg ci -m "updated i18n catalogs"`
-4. `cubicweb-ctl i18ninstance <myapplication>`
+4. `cubicweb-ctl i18ninstance <myinstance>`
 
--- a/doc/book/en/development/devweb/views.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/devweb/views.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,3 +1,6 @@
+
+.. _Views:
+
 Views
 -----
 
@@ -76,8 +79,7 @@
 
 - Using `templatable`, `content_type` and HTTP cache configuration
 
-.. code-block:: python
-
+.. sourcecode:: python
 
     class RSSView(XMLView):
         id = 'rss'
@@ -88,11 +90,9 @@
         cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
 
 
-
 - Using custom selector
 
-.. code-block:: python
-
+.. sourcecode:: python
 
     class SearchForAssociationView(EntityView):
         """view called by the edition view when the user asks
@@ -112,9 +112,9 @@
 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`` ::
+the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``:
 
-.. code-block:: python
+.. sourcecode:: python
 
    from cubicweb.view import EntityView
    from cubicweb.selectors import implements
@@ -148,7 +148,7 @@
 
 Let us now improve the primary view of a blog
 
-.. code-block:: python
+.. sourcecode:: python
 
  class BlogPrimaryView(EntityView):
      id = 'primary'
@@ -215,9 +215,9 @@
 [FILLME]
 
 
-
 XML views, binaries...
 ----------------------
+
 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:
--- a/doc/book/en/development/entityclasses/data-as-objects.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/entityclasses/data-as-objects.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -61,10 +61,10 @@
 Tne :class:`AnyEntity` class
 ----------------------------
 
-To provide a specific behavior for each entity, we have to define
-a class inheriting from `cubicweb.entities.AnyEntity`. In general, we
-define this class in a module of `mycube.entities` package of an application
-so that it will be available on both server and client side.
+To provide a specific behavior for each entity, we have to define a class
+inheriting from `cubicweb.entities.AnyEntity`. In general, we define this class
+in `mycube.entities` module (or in a submodule if we want to split code among
+multiple files) so that it will be available on both server and client side.
 
 The class `AnyEntity` is loaded dynamically from the class `Entity`
 (`cubciweb.entity`). We define a sub-class to add methods or to
--- a/doc/book/en/development/entityclasses/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/entityclasses/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -2,7 +2,7 @@
 ===============
 
 In this chapter, we will introduce the objects that are used to handle
-the data stored in the database.
+the logic associated to the data stored in the database.
 
 .. toctree::
    :maxdepth: 1
@@ -10,4 +10,4 @@
    data-as-objects
    load-sort
    interfaces
-   more
\ No newline at end of file
+   more
--- a/doc/book/en/development/entityclasses/interfaces.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/entityclasses/interfaces.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,16 +1,19 @@
 Interfaces
 ----------
 
+Same thing as object-oriented programming interfaces.
+
 XXX how to define a cw interface
 
 Declaration of interfaces implemented by a class
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 XXX __implements__
 
 
 Interfaces defined in the library
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: cubicweb.interface
-   :members:
-`````````````
+automodule:: cubicweb.interface   :members:
+
+
--- a/doc/book/en/development/entityclasses/load-sort.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/entityclasses/load-sort.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,15 +1,17 @@
+
+.. _FetchAttrs:
 
 Loaded attributes and default sorting management
 ````````````````````````````````````````````````
 
-* The class attribute `fetch_attrs` allows to defined in an entity class
-  a list of names of attributes or relations that should be automatically
-  loaded when we recover the entities of this type. In the case of relations,
+* The class attribute `fetch_attrs` allows to define in an entity class a list
+  of names of attributes or relations that should be automatically loaded when
+  entities of this type are fetched from the database. In the case of relations,
   we are limited to *subject of cardinality `?` or `1`* relations.
 
 * The class method `fetch_order(attr, var)` expects an attribute (or relation)
   name as a parameter and a variable name, and it should return a string
-  to use in the requirement `ORDER BY` of an RQL query to automatically
+  to use in the requirement `ORDERBY` of an RQL query to automatically
   sort the list of entities of such type according to this attribute, or
   `None` if we do not want to sort on the attribute given in the parameter.
   By default, the entities are sorted according to their creation date.
--- a/doc/book/en/development/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -18,3 +18,4 @@
    testing/index
    migration/index
    webstdlib/index
+   profiling/index
--- a/doc/book/en/development/migration/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/migration/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -5,19 +5,19 @@
 Migration
 =========
 
-One of the main concept in *CubicWeb* is to create incremental applications.
-For this purpose, multiple actions are provided to facilitate the improvement
-of an application, and in particular to handle the changes to be applied
-to the data model, without loosing existing data.
+One of the main design goals of *CubicWeb* was to support iterative and agile
+development. For this purpose, multiple actions are provided to facilitate the
+improvement of an instance, and in particular to handle the changes to be
+applied to the data model, without loosing existing data.
 
-The current version of an application model is provided in the file
+The current version of a cube (and of cubicweb itself) is provided in the file
 `__pkginfo__.py` as a tuple of 3 integers.
 
 Migration scripts management
 ----------------------------
 
 Migration scripts has to be located in the directory `migration` of your
-application and named accordingly:
+cube and named accordingly:
 
 ::
 
@@ -67,11 +67,8 @@
 * `interactive_mode`, boolean indicating that the script is executed in
   an interactive mode or not
 
-* `appltemplversion`, application model version of the instance
-
-* `templversion`, installed application model version
-
-* `cubicwebversion`, installed cubicweb version
+* `versions_map`, dictionary of migrated versions  (key are cubes
+  names, including 'cubicweb', values are (from version, to version)
 
 * `confirm(question)`, function asking the user and returning true
   if the user answers yes, false otherwise (always returns true in
@@ -84,16 +81,14 @@
 
 * `checkpoint`, request confirming and executing a "commit" at checking point
 
-* `repo_schema`, instance persisting schema (e.g. instance schema of the
-  current migration)
+* `schema`, instance schema (readen from the database)
 
-* `newschema`, installed schema on the file system (e.g. schema of
+* `fsschema`, installed schema on the file system (e.g. schema of
   the updated model and cubicweb)
 
-* `sqlcursor`, SQL cursor for very rare cases where it is really
-   necessary or beneficial to go through the sql
+* `repo`, repository object
 
-* `repo`, repository object
+* `session`, repository session object
 
 
 Schema migration
@@ -134,18 +129,11 @@
 * `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes
   a relation definition.
 
-* `synchronize_permissions(ertype, commit=True)`, synchronizes permissions on
-  an entity type or relation type.
-
-* `synchronize_rschema(rtype, commit=True)`, synchronizes properties and permissions
-  on a relation type.
-
-* `synchronize_eschema(etype, commit=True)`, synchronizes properties and persmissions
-  on an entity type.
-
-* `synchronize_schema(commit=True)`, synchronizes the persisting schema with the
-  updated schema (but without adding or removing new entity types, relations types
-  or even relations definitions).
+* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`,
+  synchronizes properties and/or permissions on:
+  - the whole schema if ertype is None
+  - an entity or relation type schema if ertype is a string
+  - a relation definition  if ertype is a 3-uple (subject, relation, object)
 
 * `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
   properties of a relation definition by using the named parameters of the properties
@@ -204,7 +192,7 @@
 accomplished otherwise or to repair damaged databases during interactive
 session. They are available in `repository` scripts:
 
-* `sqlexec(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query
+* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source
 * `add_entity_type_table(etype, commit=True)`
 * `add_relation_type_table(rtype, commit=True)`
 * `uninline_relation(rtype, commit=True)`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/profiling/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,55 @@
+Profiling and performance
+=========================
+
+If you feel that one of your pages takes more time than it should to be
+generated, chances are that you're making too many RQL queries.  Obviously,
+there are other reasons but experience tends to show this is the first thing to
+track down. Luckily, CubicWeb provides a configuration option to log RQL
+queries. In your ``all-in-one.conf`` file, set the **query-log-file** option::
+
+    # web application query log file
+    query-log-file=~/myapp-rql.log
+
+Then restart your application, reload your page and stop your application.
+The file ``myapp-rql.log`` now contains the list of RQL queries that were
+executed during your test. It's a simple text file containing lines such as::
+
+    Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec)
+    Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec)
+
+The structure of each line is::
+
+    <RQL QUERY> <QUERY ARGS IF ANY> -- <TIME SPENT>
+
+CubicWeb also provides the **exlog** command to examine and summarize data found
+in such a file:
+
+.. sourcecode:: sh
+
+    $ cubicweb-ctl exlog < ~/myapp-rql.log
+    0.07 50 Any A WHERE X eid %(x)s, X firstname A {}
+    0.05 50 Any A WHERE X eid %(x)s, X lastname A {}
+    0.01 1 Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E employees X, X modification_date AA {}
+    0.01 1 Any X WHERE X eid %(x)s, X owned_by U, U eid %(u)s {, }
+    0.01 1 Any B,T,P ORDERBY lower(T) WHERE B is Bookmark,B title T, B path P, B bookmarked_by U, U eid %(x)s {}
+    0.01 1 Any A,B,C,D WHERE A eid %(x)s,A name B,A creation_date C,A modification_date D {}
+
+This command sorts and uniquifies queries so that it's easy to see where
+is the hot spot that needs optimization.
+
+Do not neglect to set the **fetch_attrs** attribute you can define in your
+entity classes because it can greatly reduce the number of queries executed (see
+:ref:`FetchAttrs`).
+
+You should also know about the **profile** option in the ``all-in-on.conf``. If
+set, this option will make your application run in an `hotshot`_ session and
+store the results in the specified file.
+
+.. _hotshot: http://docs.python.org/library/hotshot.html#module-hotshot
+
+Last but no least, if you're using the PostgreSQL database backend, VACUUMing
+your database can significantly improve the performance of the queries (by
+updating the statistics used by the query optimizer). Nowadays, this is done
+automatically from time to time, but if you've just imported a large amount of
+data in your db, you will want to vacuum it (with the analyse option on). Read
+the documentation of your database for more information.
--- a/doc/book/en/development/webstdlib/basetemplates.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/basetemplates.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -159,7 +159,7 @@
 .. _TheMainTemplate:
 
 TheMainTemplate is responsible for the general layout of the entire application.
-It defines the template of ``id = main`` that is used by the application.
+It defines the template of ``id = main`` that is used by the instance.
 
 The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`)
 builds the page based on the following pattern:
--- a/doc/book/en/development/webstdlib/baseviews.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/baseviews.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -12,33 +12,6 @@
 
 HTML views
 ~~~~~~~~~~
-*oneline*
-    This is a hyper linked *text* view. Similar to the `secondary` view,
-    but called when we want the view to stand on a single line, or just
-    get a brief view. By default this view uses the
-    parameter `MAX_LINE_CHAR` to control the result size.
-
-*secondary*
-    This is a combinaison of an icon and a *oneline* view.
-    By default it renders the two first attributes of the entity as a
-    clickable link redirecting to the primary view.
-
-*incontext, outofcontext*
-    Similar to the `secondary` view, but called when an entity is considered
-    as in or out of context. By default it respectively returns the result of
-    `textincontext` and `textoutofcontext` wrapped 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 (`<ul>`)
-    and call the view `listitem` for each entity of the result set.
-
-*listitem*
-    This view redirects by default to the `outofcontext` view.
-
-
 Special views
 `````````````
 
@@ -54,11 +27,41 @@
     This view is the default view used when nothing needs to be rendered.
     It is always applicable and it does not return anything
 
-Text views
-~~~~~~~~~~
+Entity views
+````````````
+*incontext, outofcontext*
+    Those are used to display a link to an entity, depending if the entity is
+    considered as 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 (`<ul>`)
+    and call the view `listitem` for each entity of the result set.
+
+*listitem*
+    This view redirects by default to the `outofcontext` view.
+
+*adaptedlist*
+    This view displays a list of entities of the same type, in HTML section (`<div>`)
+    and call the view `adaptedlistitem` for each entity of the result set.
+
+*adaptedlistitem*
+    This view redirects by default to the `outofcontext` view.
+
+Text entity views
+~~~~~~~~~~~~~~~~~
 *text*
-    This is the simplest text view for an entity. It displays the
-    title of an entity. It should not contain HTML.
+    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
--- a/doc/book/en/development/webstdlib/editcontroller.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/editcontroller.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,5 @@
 .. -*- coding: utf-8 -*-
+
 The 'edit' controller (:mod:`cubicweb.web.views.editcontroller`)
 ----------------------------------------------------------------
 
--- a/doc/book/en/development/webstdlib/primary.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/primary.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -15,7 +15,7 @@
 Let's have a quick look at the EntityView ``PrimaryView`` as well as
 its rendering method
 
-.. code-block:: python
+.. sourcecode:: python
 
     class PrimaryView(EntityView):
         """the full view of an non final entity"""
--- a/doc/book/en/development/webstdlib/startup.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/startup.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -9,5 +9,5 @@
     a result set to apply to.
 
 *schema*
-    A view dedicated to the display of the schema of the application
+    A view dedicated to the display of the schema of the instance
 
--- a/doc/book/en/development/webstdlib/urlpublish.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/urlpublish.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,5 @@
 .. -*- coding: utf-8 -*-
+
 URL Rewriting (:mod:`cubicweb.web.views.urlpublish`) and (:mod:`cubicweb.web.views.urlrewrite`)
 ------------------------------------------------------------------------------------------------
 
--- a/doc/book/en/development/webstdlib/xmlrss.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/development/webstdlib/xmlrss.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -1,3 +1,5 @@
+.. _XmlAndRss:
+
 XML and RSS views (:mod:`cubicweb.web.views.xmlrss`)
 ----------------------------------------------------
 
--- a/doc/book/en/intro/concepts/index.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/intro/concepts/index.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -64,11 +64,12 @@
 
 On a Unix system, the instances are usually stored in the directory
 :file:`/etc/cubicweb.d/`. During development, the :file:`~/etc/cubicweb.d/`
-directory is looked up, as well as the paths in :envvar:`CW_REGISTRY`
+directory is looked up, as well as the paths in :envvar:`CW_INSTANCES_DIR`
 environment variable.
 
-The term application can refer to an instance or to a cube, depending on the
-context. This book will try to avoid using this term and use *cube* and
+The term application is used to refer at "something that should do something as a
+whole", eg more like a project and so can refer to an instance or to a cube,
+depending on the context. This book will try to use *application*, *cube* and
 *instance* as appropriate.
 
 Data Repository
@@ -154,7 +155,7 @@
 identifier in the same registry, At runtime, appobjects are selected in the
 vregistry according to the context.
 
-Application objects are stored in the registry using a two level hierarchy :
+Application objects are stored in the registry using a two-level hierarchy :
 
   object's `__registry__` : object's `id` : [list of app objects]
 
@@ -166,7 +167,7 @@
 At startup, the `registry` or registers base, inspects a number of directories
 looking for compatible classes definition. After a recording process, the objects
 are assigned to registers so that they can be selected dynamically while the
-application is running.
+instance is running.
 
 Selectors
 ~~~~~~~~~
@@ -215,7 +216,7 @@
 
 **No need for a complicated ORM when you have a powerful query language**
 
-All the persistant data in a CubicWeb application is retrieved and modified by using the
+All the persistent data in a CubicWeb instance is retrieved and modified by using the
 Relation Query Language.
 
 This query language is inspired by SQL but is on a higher level in order to
--- a/doc/book/en/intro/tutorial/components.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/intro/tutorial/components.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -11,7 +11,7 @@
 A library of standard cubes are available from `CubicWeb Forge`_
 Cubes provide entities and views.
 
-The available application entities are:
+The available application entities in standard cubes are:
 
 * addressbook: PhoneNumber and PostalAddress
 
@@ -39,11 +39,12 @@
 * 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 application just change the line in the
+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::
@@ -51,7 +52,7 @@
     __use__ = ('comment',)
 
 will make the ``Comment`` entity available in your ``BlogDemo``
-application.
+cube.
 
 Change the schema to add a relationship between ``BlogEntry`` and
 ``Comment`` and you are done. Since the comment cube defines the
@@ -67,13 +68,12 @@
 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 migration Python shell. (see
-:ref:`cubicweb-ctl` for more details))
-As you modified a relation from the `BlogEntry` schema,
-run the following command:
+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:
+
 ::
 
-  synchronize_rschema('BlogEntry')
+  add_cube('comment')
 
-You can now start your application and add comments to each
-`BlogEntry`.
+You can now start your instance and comment your blog entries.
--- a/doc/book/en/intro/tutorial/conclusion.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/intro/tutorial/conclusion.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -3,15 +3,13 @@
 What's next?
 ------------
 
-We demonstrated how from a straight out of the box *CubicWeb*
-installation, you can build your web-application based on a
-schema. It's all already there: views, templates, permissions,
-etc. The step forward is now for you to customize according
-to your needs.
+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.
 
-More than a web application, many features are available to
-extend your application, for example: RSS channel integration
-(:ref:`rss`), hooks (:ref:`hooks`), support of sources such as
-Google App Engine (:ref:`gaecontents`) and lots of others to
-discover through our book.
+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.
 
--- a/doc/book/en/intro/tutorial/create-cube.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/intro/tutorial/create-cube.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -14,7 +14,7 @@
 
 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:`cubesConcepts`.
+a directory named ``blog`` reflecting the structure described in :ref:`Concepts`.
 
 .. _DefineDataModel:
 
@@ -60,39 +60,39 @@
 Create your instance
 --------------------
 
-To use this cube as an application and create a new instance named ``blogdemo``, do::
+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.
 
-Welcome to your web application
+Welcome to your web instance
 -------------------------------
 
-Start your application in debug mode with the following command: ::
+Start your instance in debug mode with the following command: ::
 
   cubicweb-ctl start -D blogdemo
 
 
-You can now access your web application to create blogs and post messages
+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 application will not allow anonymous
-users to enter the application. To login, you need then use the admin account
+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 application
+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* franework managed all aspects of
-the web application based on the schema provided at first.
+Please notice that so far, the *CubicWeb* framework managed all aspects of
+the web application based on the schema provided at the beginning.
 
 
 Add entities
@@ -222,7 +222,7 @@
 
 To do so, please apply the following changes:
 
-.. code-block:: python
+.. sourcecode:: python
 
   from cubicweb.web.views import baseviews
 
@@ -251,7 +251,7 @@
             self.render_entity_relations(entity, siderelations)
 
 .. note::
-  When a view is modified, it is not required to restart the application
+  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.
 
@@ -268,6 +268,7 @@
 The view has a ``self.w()`` method that is used to output data, in our
 example HTML output.
 
-You can find more details about views and selectors in :ref:`ViewDefinition`.
+.. note::
+   You can find more details about views and selectors in :ref:`ViewDefinition`.
 
 
--- a/doc/book/en/intro/tutorial/maintemplate.rst	Fri Jul 31 14:18:53 2009 +0200
+++ b/doc/book/en/intro/tutorial/maintemplate.rst	Fri Jul 31 14:25:30 2009 +0200
@@ -36,15 +36,15 @@
 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 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:
 
-.. code-block :: python
+.. sourcecode:: python
 
   class MyBlogHTMLPageHeader(HTMLPageHeader):
 
@@ -89,9 +89,9 @@
 ````````````````
 
 If you want to change the footer for example, look
-for HTMLPageFooter and override it in your views file as in: ::
+for HTMLPageFooter and override it in your views file as in:
 
-..code-block :: python
+.. sourcecode:: python
 
   from cubicweb.web.views.basetemplates import HTMLPageFooter
 
--- a/entities/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/entities/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -15,7 +15,6 @@
 from cubicweb import Unauthorized, typed_eid
 from cubicweb.entity import Entity
 from cubicweb.utils import dump_class
-from cubicweb.schema import FormatConstraint
 
 from cubicweb.interfaces import IBreadCrumbs, IFeed
 
@@ -242,12 +241,12 @@
 
     @obsolete('use EntityFieldsForm.subject_relation_vocabulary')
     def subject_relation_vocabulary(self, rtype, limit):
-        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        form = self.vreg.select('forms', 'edition', self.req, entity=self)
         return form.subject_relation_vocabulary(rtype, limit)
 
     @obsolete('use EntityFieldsForm.object_relation_vocabulary')
     def object_relation_vocabulary(self, rtype, limit):
-        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        form = self.vreg.select('forms', 'edition', self.req, entity=self)
         return form.object_relation_vocabulary(rtype, limit)
 
     @obsolete('use AutomaticEntityForm.[e]relations_by_category')
--- a/entities/schemaobjs.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/entities/schemaobjs.py	Fri Jul 31 14:25:30 2009 +0200
@@ -25,8 +25,6 @@
     def dc_long_title(self):
         stereotypes = []
         _ = self.req._
-        if self.meta:
-            stereotypes.append(_('meta'))
         if self.final:
             stereotypes.append(_('final'))
         if stereotypes:
@@ -48,8 +46,6 @@
     def dc_long_title(self):
         stereotypes = []
         _ = self.req._
-        if self.meta:
-            stereotypes.append(_('meta'))
         if self.symetric:
             stereotypes.append(_('symetric'))
         if self.inlined:
--- a/entity.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/entity.py	Fri Jul 31 14:25:30 2009 +0200
@@ -23,13 +23,9 @@
 from cubicweb.appobject import AppRsetObject
 from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype
 
-try:
-    from cubicweb.common.uilib import printable_value, soup2xhtml
-    from cubicweb.common.mixins import MI_REL_TRIGGERS
-    from cubicweb.common.mttransforms import ENGINE
-except ImportError:
-    # missing -common
-    MI_REL_TRIGGERS = {}
+from cubicweb.common.uilib import printable_value, soup2xhtml
+from cubicweb.common.mixins import MI_REL_TRIGGERS
+from cubicweb.common.mttransforms import ENGINE
 
 _marker = object()
 
@@ -368,13 +364,20 @@
     def has_perm(self, action):
         return self.e_schema.has_perm(self.req, action, self.eid)
 
-    def view(self, vid, __registry='views', **kwargs):
+    def view(self, vid, **kwargs):
         """shortcut to apply a view on this entity"""
-        return self.vreg.render(__registry, vid, self.req, rset=self.rset,
+        return self.vreg.render(vid, self.req, rset=self.rset,
                                 row=self.row, col=self.col, **kwargs)
 
-    def absolute_url(self, method=None, **kwargs):
+    def absolute_url(self, *args, **kwargs):
         """return an absolute url to view this entity"""
+        # use *args since we don't want first argument to be "anonymous" to
+        # avoid potential clash with kwargs
+        if args:
+            assert len(args) == 1, 'only 0 or 1 non-named-argument expected'
+            method = args[0]
+        else:
+            method = None
         # in linksearch mode, we don't want external urls else selecting
         # the object for use in the relation is tricky
         # XXX search_state is web specific
@@ -478,7 +481,7 @@
         assert self.has_eid()
         execute = self.req.execute
         for rschema in self.e_schema.subject_relations():
-            if rschema.meta or rschema.is_final():
+            if rschema.is_final() or rschema.meta:
                 continue
             # skip already defined relations
             if getattr(self, rschema.type):
@@ -726,7 +729,7 @@
         interpreted as a separator in case vocabulary results are grouped
         """
         from logilab.common.testlib import mock_object
-        form = self.vreg.select_object('forms', 'edition', self.req, entity=self)
+        form = self.vreg.select('forms', 'edition', self.req, entity=self)
         field = mock_object(name=rtype, role=role)
         return form.form_field_vocabulary(field, limit)
 
--- a/etwist/request.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/etwist/request.py	Fri Jul 31 14:25:30 2009 +0200
@@ -38,7 +38,7 @@
         self._headers = req.headers
 
     def base_url(self):
-        """return the root url of the application"""
+        """return the root url of the instance"""
         return self._base_url
 
     def http_method(self):
@@ -47,7 +47,7 @@
 
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
-        to the application's root, but some other normalization may be needed
+        to the instance's root, but some other normalization may be needed
         so that the returned path may be used to compare to generated urls
 
         :param includeparams:
--- a/etwist/server.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/etwist/server.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-"""twisted server for CubicWeb web applications
+"""twisted server for CubicWeb web instances
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -106,10 +106,7 @@
                 self.pyro_listen_timeout = 0.02
                 start_task(1, self.pyro_loop_event)
             self.appli.repo.start_looping_tasks()
-        try:
-            self.url_rewriter = self.appli.vreg.select_component('urlrewriter')
-        except ObjectNotFound:
-            self.url_rewriter = None
+        self.url_rewriter = self.appli.vreg.select_object('components', 'urlrewriter')
         interval = min(config['cleanup-session-time'] or 120,
                        config['cleanup-anonymous-session-time'] or 720) / 2.
         start_task(interval, self.appli.session_handler.clean_sessions)
@@ -274,9 +271,9 @@
 
 # This part gets run when you run this file via: "twistd -noy demo.py"
 def main(appid, cfgname):
-    """Starts an cubicweb  twisted server for an application
+    """Starts an cubicweb  twisted server for an instance
 
-    appid: application's identifier
+    appid: instance's identifier
     cfgname: name of the configuration to use (twisted or all-in-one)
     """
     from cubicweb.cwconfig import CubicWebConfiguration
--- a/etwist/twconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/etwist/twconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,9 +1,9 @@
 """twisted server configurations:
 
-* the "twisted" configuration to get a web application running in a standalone
+* the "twisted" configuration to get a web instance running in a standalone
   twisted web server which talk to a repository server using Pyro
 
-* the "all-in-one" configuration to get a web application running in a twisted
+* the "all-in-one" configuration to get a web instance running in a twisted
   web server integrating a repository server in the same process (only available
   if the repository part of the software is installed
 
@@ -19,7 +19,7 @@
 from cubicweb.web.webconfig import WebConfiguration, merge_options, Method
 
 class TwistedConfiguration(WebConfiguration):
-    """web application (in a twisted web server) client of a RQL server"""
+    """web instance (in a twisted web server) client of a RQL server"""
     name = 'twisted'
 
     options = merge_options((
@@ -81,7 +81,7 @@
     from cubicweb.server.serverconfig import ServerConfiguration
 
     class AllInOneConfiguration(TwistedConfiguration, ServerConfiguration):
-        """repository and web application in the same twisted process"""
+        """repository and web instance in the same twisted process"""
         name = 'all-in-one'
         repo_method = 'inmemory'
         options = merge_options(TwistedConfiguration.options
--- a/etwist/twctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/etwist/twctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -64,8 +64,8 @@
     from cubicweb.server import serverctl
 
     class AllInOneCreateHandler(serverctl.RepositoryCreateHandler, TWCreateHandler):
-        """configuration to get a web application running in a twisted web
-        server integrating a repository server in the same process
+        """configuration to get an instance running in a twisted web server
+        integrating a repository server in the same process
         """
         cfgname = 'all-in-one'
 
--- a/ext/rest.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/ext/rest.py	Fri Jul 31 14:25:30 2009 +0200
@@ -31,6 +31,7 @@
 
 from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape
 
+from cubicweb import UnknownEid
 from cubicweb.ext.html4zope import Writer
 
 # We provide our own parser as an attempt to get rid of
@@ -68,8 +69,13 @@
         return [prb], [msg]
     # Base URL mainly used by inliner.pep_reference; so this is correct:
     context = inliner.document.settings.context
-    refedentity = context.req.eid_rset(eid_num).get_entity(0, 0)
-    ref = refedentity.absolute_url()
+    try:
+        refedentity = context.req.eid_rset(eid_num).get_entity(0, 0)
+    except UnknownEid:
+        ref = '#'
+        rest += u' ' + context.req._('(UNEXISTANT EID)')
+    else:
+        ref = refedentity.absolute_url()
     set_classes(options)
     return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref,
                             **options)], []
--- a/goa/appobjects/components.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/appobjects/components.py	Fri Jul 31 14:25:30 2009 +0200
@@ -45,7 +45,7 @@
     content_type = 'image/png'
     def call(self):
         """display global schema information"""
-        skipmeta = not int(self.req.form.get('withmeta', 0))
+        skipmeta = int(self.req.form.get('skipmeta', 1))
         if skipmeta:
             url = self.build_url('data/schema.png')
         else:
@@ -72,7 +72,7 @@
             continue
         etype = eschema.type
         label = display_name(req, etype, 'plural')
-        view = self.vreg.select_view('list', req, req.etype_rset(etype))
+        view = self.vreg.select('views', 'list', req, req.etype_rset(etype))
         url = view.url()
         etypelink = u'&nbsp;<a href="%s">%s</a>' % (xml_escape(url), label)
         yield (label, etypelink, self.add_entity_link(eschema, req))
--- a/goa/dbinit.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/dbinit.py	Fri Jul 31 14:25:30 2009 +0200
@@ -20,7 +20,7 @@
         try:
             group = Get(key)
         except datastore_errors.EntityNotFoundError:
-            raise Exception('can\'t find required group %s, is your application '
+            raise Exception('can\'t find required group %s, is your instance '
                             'correctly initialized (eg did you run the '
                             'initialization script) ?' % groupname)
         _GROUP_CACHE[groupname] = group
@@ -86,14 +86,13 @@
 def init_persistent_schema(ssession, schema):
     execute = ssession.unsafe_execute
     rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,'
-           'X final FALSE, X meta %(meta)s')
+           'X final FALSE')
     eschema = schema.eschema('CWEType')
-    execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description),
-                  'meta': eschema.meta})
+    execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description)})
     for eschema in schema.entities():
         if eschema.is_final() or eschema == 'CWEType':
             continue
-        execute(rql, {'name': unicode(eschema), 'meta': eschema.meta,
+        execute(rql, {'name': unicode(eschema),
                       'descr': unicode(eschema.description)})
 
 def insert_versions(ssession, config):
--- a/goa/dbmyams.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/dbmyams.py	Fri Jul 31 14:25:30 2009 +0200
@@ -167,7 +167,7 @@
     SCHEMAS_LIB_DIRECTORY = join(CW_SOFTWARE_ROOT, 'schemas')
 
 def load_schema(config, schemaclasses=None, extrahook=None):
-    """high level method to load all the schema for a lax application"""
+    """high level method to load all the schema for a lax instance"""
     # IMPORTANT NOTE: dbmodel schemas must be imported **BEFORE**
     # the loader is instantiated because this is where the dbmodels
     # are registered in the yams schema
--- a/goa/gaesource.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/gaesource.py	Fri Jul 31 14:25:30 2009 +0200
@@ -155,7 +155,7 @@
         return rqlst
 
     def set_schema(self, schema):
-        """set the application'schema"""
+        """set the instance'schema"""
         self.interpreter = RQLInterpreter(schema)
         self.schema = schema
         if 'CWUser' in schema and not self.repo.config['use-google-auth']:
--- a/goa/goaconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/goaconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -17,7 +17,7 @@
 from cubicweb.goa.dbmyams import load_schema
 
 UNSUPPORTED_OPTIONS = set(('connections-pool-size',
-                           'pyro-port', 'pyro-id', 'pyro-application-id',
+                           'pyro-port', 'pyro-id', 'pyro-instance-id',
                            'pyro-ns-host', 'pyro-ns-port', 'pyro-ns-group',
                            'https-url', 'host', 'pid-file', 'uid', 'base-url', 'log-file',
                            'smtp-host', 'smtp-port',
@@ -30,41 +30,41 @@
 # * check auth-mode=http + fix doc (eg require use-google-auth = False)
 
 class GAEConfiguration(ServerConfiguration, WebConfiguration):
-    """repository and web application in the same twisted process"""
+    """repository and web instance in Google AppEngine environment"""
     name = 'app'
     repo_method = 'inmemory'
     options = merge_options((
         ('included-cubes',
          {'type' : 'csv',
           'default': [],
-          'help': 'list of db model based cubes used by the application.',
+          'help': 'list of db model based cubes used by the instance.',
           'group': 'main', 'inputlevel': 1,
           }),
         ('included-yams-cubes',
          {'type' : 'csv',
           'default': [],
-          'help': 'list of yams based cubes used by the application.',
+          'help': 'list of yams based cubes used by the instance.',
           'group': 'main', 'inputlevel': 1,
           }),
         ('use-google-auth',
          {'type' : 'yn',
           'default': True,
-          'help': 'does this application rely on google authentication service or not.',
+          'help': 'does this instance rely on google authentication service or not.',
           'group': 'main', 'inputlevel': 1,
           }),
         ('schema-type',
          {'type' : 'choice', 'choices': ('yams', 'dbmodel'),
           'default': 'yams',
-          'help': 'does this application is defining its schema using yams or db model.',
+          'help': 'does this instance is defining its schema using yams or db model.',
           'group': 'main', 'inputlevel': 1,
           }),
         # overriden options
         ('query-log-file',
          {'type' : 'string',
           'default': None,
-          'help': 'web application query log file: DON\'T SET A VALUE HERE WHEN '
-          'UPLOADING YOUR APPLICATION. This should only be used to analyse '
-          'queries issued by your application in the development environment.',
+          'help': 'web instance query log file: DON\'T SET A VALUE HERE WHEN '
+          'UPLOADING YOUR INSTANCE. This should only be used to analyse '
+          'queries issued by your instance in the development environment.',
           'group': 'main', 'inputlevel': 2,
           }),
         ('anonymous-user',
@@ -86,7 +86,7 @@
     cube_vobject_path = WebConfiguration.cube_vobject_path | ServerConfiguration.cube_vobject_path
 
     # use file system schema
-    bootstrap_schema = read_application_schema = False
+    bootstrap_schema = read_instance_schema = False
     # schema is not persistent, don't load schema hooks (unavailable)
     schema_hooks = False
     # no user workflow for now
@@ -130,7 +130,7 @@
         return self._cubes
 
     def vc_config(self):
-        """return CubicWeb's engine and application's cube versions number"""
+        """return CubicWeb's engine and instance's cube versions number"""
         return {}
 
     # overriden from cubicweb web configuration
--- a/goa/goactl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/goactl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -172,13 +172,13 @@
 
 
 class NewGoogleAppCommand(Command):
-    """Create a new google appengine application.
+    """Create a new google appengine instance.
 
-    <application directory>
-      the path to the appengine application directory
+    <instance directory>
+      the path to the appengine instance directory
     """
     name = 'newgapp'
-    arguments = '<application directory>'
+    arguments = '<instance directory>'
 
     def run(self, args):
         if len(args) != 1:
@@ -187,7 +187,7 @@
         appldir = normpath(abspath(appldir))
         appid = basename(appldir)
         context = {'appname': appid}
-        # goa application'skeleton
+        # goa instance'skeleton
         copy_skeleton(join(CW_SOFTWARE_ROOT, 'goa', 'skel'),
                       appldir, context, askconfirm=True)
         # cubicweb core dependancies
--- a/goa/goavreg.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/goavreg.py	Fri Jul 31 14:25:30 2009 +0200
@@ -37,7 +37,7 @@
                             'cubicweb.goa.appobjects')
         for cube in reversed(self.config.cubes()):
             self.load_cube(cube)
-        self.load_application(applroot)
+        self.load_instance(applroot)
 
     def load_directory(self, directory, cube, skip=()):
         for filename in listdir(directory):
@@ -49,7 +49,7 @@
                         cube in self.config['included-cubes'],
                         cube)
 
-    def load_application(self, applroot):
+    def load_instance(self, applroot):
         self._auto_load(applroot, self.config['schema-type'] == 'dbmodel')
 
     def _import(self, modname):
--- a/goa/overrides/toolsutils.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/overrides/toolsutils.py	Fri Jul 31 14:25:30 2009 +0200
@@ -17,7 +17,7 @@
     return result
 
 def read_config(config_file):
-    """read the application configuration from a file and return it as a
+    """read the instance configuration from a file and return it as a
     dictionnary
 
     :type config_file: str
--- a/goa/skel/loader.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/skel/loader.py	Fri Jul 31 14:25:30 2009 +0200
@@ -12,11 +12,11 @@
     from cubicweb.goa.goaconfig import GAEConfiguration
     from cubicweb.goa.dbinit import create_user, create_groups
 
-    # compute application's root directory
+    # compute instance's root directory
     APPLROOT = dirname(abspath(__file__))
     # apply monkey patches first
     goa.do_monkey_patch()
-    # get application's configuration (will be loaded from app.conf file)
+    # get instance's configuration (will be loaded from app.conf file)
     GAEConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
     config = GAEConfiguration('toto', APPLROOT)
     # create default groups
--- a/goa/skel/main.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/skel/main.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-"""module defining the root handler for a lax application. You should not have
+"""module defining the root handler for a lax instance. You should not have
 to change anything here.
 
 :organization: Logilab
@@ -8,7 +8,7 @@
 """
 __docformat__ = "restructuredtext en"
 
-# compute application's root directory
+# compute instance's root directory
 from os.path import dirname, abspath
 APPLROOT = dirname(abspath(__file__))
 
@@ -16,7 +16,7 @@
 from cubicweb import goa
 goa.do_monkey_patch()
 
-# get application's configuration (will be loaded from app.conf file)
+# get instance's configuration (will be loaded from app.conf file)
 from cubicweb.goa.goaconfig import GAEConfiguration
 GAEConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
 config = GAEConfiguration('toto', APPLROOT)
@@ -29,13 +29,13 @@
 # before schema loading
 import custom
 
-# load application'schema
+# load instance'schema
 vreg.schema = config.load_schema()
 
 # load dynamic objects
 vreg.load(APPLROOT)
 
-# call the postinit so custom get a chance to do application specific stuff
+# call the postinit so custom get a chance to do instance specific stuff
 custom.postinit(vreg)
 
 from cubicweb.wsgi.handler import CubicWebWSGIApplication
--- a/goa/skel/schema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/skel/schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,6 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from cubicweb.schema import format_constraint
 
 class Blog(EntityType):
     title = String(maxsize=50, required=True)
@@ -14,8 +13,6 @@
 class BlogEntry(EntityType):
     title = String(maxsize=100, required=True)
     publish_date = Date(default='TODAY')
-    text_format = String(meta=True, internationalizable=True, maxsize=50,
-                         default='text/rest', constraints=[format_constraint])
-    text = String(fulltextindexed=True)
+    text = RichString(fulltextindexed=True)
     category = String(vocabulary=('important','business'))
     entry_of = SubjectRelation('Blog', cardinality='?*')
--- a/goa/test/unittest_editcontroller.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/test/unittest_editcontroller.py	Fri Jul 31 14:25:30 2009 +0200
@@ -37,8 +37,7 @@
         self.ctrl = self.get_ctrl(self.req)
 
     def get_ctrl(self, req):
-        return self.vreg.select(self.vreg.registry_objects('controllers', 'edit'),
-                                req=req, appli=self)
+        return self.vreg.select('controllers', 'edit', req=req, appli=self)
 
     def publish(self, req):
         assert req is self.ctrl.req
--- a/goa/test/unittest_views.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/test/unittest_views.py	Fri Jul 31 14:25:30 2009 +0200
@@ -51,7 +51,7 @@
     def test_django_index(self):
         self.vreg.render('views', 'index', self.req, rset=None)
 
-for vid in ('primary', 'secondary', 'oneline', 'incontext', 'outofcontext', 'text'):
+for vid in ('primary', 'oneline', 'incontext', 'outofcontext', 'text'):
     setattr(SomeViewsTC, 'test_%s'%vid, lambda self, vid=vid: self.blog.view(vid))
 
 if __name__ == '__main__':
--- a/goa/testlib.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/testlib.py	Fri Jul 31 14:25:30 2009 +0200
@@ -131,7 +131,7 @@
                 self.vreg.load_module(module)
         for cls in self.MODEL_CLASSES:
             self.vreg.load_object(cls)
-        self.session_manager = self.vreg.select_component('sessionmanager')
+        self.session_manager = self.vreg.select('components', 'sessionmanager')
         if need_ds_init:
             # create default groups and create entities according to the schema
             create_groups()
--- a/goa/tools/generate_schema_img.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/tools/generate_schema_img.py	Fri Jul 31 14:25:30 2009 +0200
@@ -8,6 +8,7 @@
 import sys
 from os.path import dirname, abspath, join
 from yams import schema2dot
+from cubicweb.web.views.schema import SKIP_TYPES
 
 APPLROOT = abspath(join(dirname(abspath(__file__)), '..'))
 
@@ -22,9 +23,8 @@
 skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
 path = join(APPLROOT, 'data', 'schema.png')
 schema2dot.schema2dot(schema, path, #size=size,
-                      skiprels=skip_rels, skipmeta=True)
+                      skiptypes=SKIP_TYPES)
 print 'generated', path
 path = join(APPLROOT, 'data', 'metaschema.png')
-schema2dot.schema2dot(schema, path, #size=size,
-                      skiprels=skip_rels, skipmeta=False)
+schema2dot.schema2dot(schema, path)
 print 'generated', path
--- a/goa/tools/laxctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/goa/tools/laxctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -19,6 +19,8 @@
 from logilab.common.clcommands import Command, register_commands, main_run
 
 from cubicweb.common.uilib import remove_html_tags
+from cubicweb.web.views.schema import SKIP_TYPES
+
 APPLROOT = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), '..'))
 
 
@@ -57,19 +59,17 @@
         assert not args, 'no argument expected'
         from yams import schema2dot
         schema = self.vreg.schema
-        skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
         path = osp.join(APPLROOT, 'data', 'schema.png')
         schema2dot.schema2dot(schema, path, #size=size,
-                              skiprels=skip_rels, skipmeta=True)
+                              skiptypes=SKIP_TYPES)
         print 'generated', path
         path = osp.join(APPLROOT, 'data', 'metaschema.png')
-        schema2dot.schema2dot(schema, path, #size=size,
-                              skiprels=skip_rels, skipmeta=False)
+        schema2dot.schema2dot(schema, path)
         print 'generated', path
 
 
 class PopulateDataDirCommand(LaxCommand):
-    """populate application's data directory according to used cubes"""
+    """populate instance's data directory according to used cubes"""
     name = 'populatedata'
 
     def _run(self, args):
@@ -118,7 +118,7 @@
 
 
 class URLCommand(LaxCommand):
-    """abstract class for commands doing stuff by accessing the web application
+    """abstract class for commands doing stuff by accessing the web instance
     """
     min_args = max_args = 1
     arguments = '<site url>'
--- a/hercule.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/hercule.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-"""RQL client for cubicweb, connecting to application using pyro
+"""RQL client for cubicweb, connecting to instance using pyro
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -79,7 +79,7 @@
         'debug' :        "Others",
         })
 
-    def __init__(self, application=None, user=None, password=None,
+    def __init__(self, instance=None, user=None, password=None,
                  host=None, debug=0):
         CLIHelper.__init__(self, os.path.join(os.environ["HOME"], ".erqlhist"))
         self.cnx = None
@@ -92,12 +92,12 @@
         self.autocommit = False
         self._last_result = None
         self._previous_lines = []
-        if application is not None:
-            self.do_connect(application, user, password, host)
+        if instance is not None:
+            self.do_connect(instance, user, password, host)
         self.do_debug(debug)
 
-    def do_connect(self, application, user=None, password=None, host=None):
-        """connect to an cubicweb application"""
+    def do_connect(self, instance, user=None, password=None, host=None):
+        """connect to an cubicweb instance"""
         from cubicweb.dbapi import connect
         if user is None:
             user = raw_input('login: ')
@@ -107,16 +107,16 @@
         if self.cnx is not None:
             self.cnx.close()
         self.cnx = connect(login=user, password=password, host=host,
-                           database=application)
+                           database=instance)
         self.schema = self.cnx.get_schema()
         self.cursor = self.cnx.cursor()
         # add entities types to the completion commands
         self._completer.list = (self.commands.keys() +
                                 self.schema.entities() + ['Any'])
-        print _('You are now connected to %s') % application
+        print _('You are now connected to %s') % instance
 
 
-    help_do_connect = ('connect', "connect <application> [<user> [<password> [<host>]]]",
+    help_do_connect = ('connect', "connect <instance> [<user> [<password> [<host>]]]",
                        _(do_connect.__doc__))
 
     def do_debug(self, debug=1):
@@ -142,9 +142,9 @@
     help_do_description = ('description', "description", _(do_description.__doc__))
 
     def do_schema(self, name=None):
-        """display information about the application schema """
+        """display information about the instance schema """
         if self.cnx is None:
-            print _('You are not connected to an application !')
+            print _('You are not connected to an instance !')
             return
         done = None
         if name is None:
@@ -189,7 +189,7 @@
         else, stores the query line and waits for the suite
         """
         if self.cnx is None:
-            print _('You are not connected to an application !')
+            print _('You are not connected to an instance !')
             return
         # append line to buffer
         self._previous_lines.append(stripped_line)
@@ -232,11 +232,11 @@
 class CubicWebClientCommand(Command):
     """A command line querier for CubicWeb, using the Relation Query Language.
 
-    <application>
-      identifier of the application to connect to
+    <instance>
+      identifier of the instance to connect to
     """
     name = 'client'
-    arguments = '<application>'
+    arguments = '<instance>'
     options = CONNECT_OPTIONS + (
         ("verbose",
          {'short': 'v', 'type' : 'int', 'metavar': '<level>',
--- a/i18n/entities.pot	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-msgid "__msg state changed"
-msgstr ""
-
-msgid "managers"
-msgstr ""
-
-msgid "users"
-msgstr ""
-
-msgid "guests"
-msgstr ""
-
-msgid "owners"
-msgstr ""
-
-msgid "read_perm"
-msgstr ""
-
-msgid "add_perm"
-msgstr ""
-
-msgid "update_perm"
-msgstr ""
-
-msgid "delete_perm"
-msgstr ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i18n/static-messages.pot	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,11 @@
+msgid "read_perm"
+msgstr ""
+
+msgid "add_perm"
+msgstr ""
+
+msgid "update_perm"
+msgstr ""
+
+msgid "delete_perm"
+msgstr ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.4.0_Any.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,2 @@
+drop_attribute('CWEType', 'meta')
+drop_attribute('CWRType', 'meta')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.4.0_common.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,6 @@
+from os.path import join
+from cubicweb.toolsutils import create_dir
+
+option_renamed('pyro-application-id', 'pyro-instance-id')
+
+create_dir(join(config.appdatahome, 'backup'))
--- a/misc/migration/bootstrapmigration_repository.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py	Fri Jul 31 14:25:30 2009 +0200
@@ -10,49 +10,69 @@
 
 applcubicwebversion, cubicwebversion = versions_map['cubicweb']
 
+if applcubicwebversion < (3, 4, 0) and cubicwebversion >= (3, 4, 0):
+    from cubicweb import RepositoryError
+    from cubicweb.server.hooks import uniquecstrcheck_before_modification
+    session.set_shared_data('do-not-insert-cwuri', True)
+    repo.hm.unregister_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
+    repo.hm.unregister_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
+    add_relation_type('cwuri')
+    base_url = session.base_url()
+    # use an internal session since some entity might forbid modifications to admin
+    isession = repo.internal_session()
+    for eid, in rql('Any X', ask_confirm=False):
+        try:
+            isession.execute('SET X cwuri %(u)s WHERE X eid %(x)s',
+                             {'x': eid, 'u': base_url + u'eid/%s' % eid})
+        except RepositoryError:
+            print 'unable to set cwuri for', eid, session.describe(eid)
+    isession.commit()
+    repo.hm.register_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
+    repo.hm.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
+    session.set_shared_data('do-not-insert-cwuri', False)
+
 if applcubicwebversion < (3, 2, 2) and cubicwebversion >= (3, 2, 1):
-   from base64 import b64encode
-   for table in ('entities', 'deleted_entities'):
-      for eid, extid in sql('SELECT eid, extid FROM %s WHERE extid is NOT NULL'
-                            % table, ask_confirm=False):
-         sql('UPDATE %s SET extid=%%(extid)s WHERE eid=%%(eid)s' % table,
-             {'extid': b64encode(extid), 'eid': eid}, ask_confirm=False)
-   checkpoint()
+    from base64 import b64encode
+    for table in ('entities', 'deleted_entities'):
+        for eid, extid in sql('SELECT eid, extid FROM %s WHERE extid is NOT NULL'
+                              % table, ask_confirm=False):
+            sql('UPDATE %s SET extid=%%(extid)s WHERE eid=%%(eid)s' % table,
+                {'extid': b64encode(extid), 'eid': eid}, ask_confirm=False)
+    checkpoint()
 
 if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0):
-   add_cube('card', update_database=False)
+    add_cube('card', update_database=False)
 
 if applcubicwebversion < (2, 47, 0) and cubicwebversion >= (2, 47, 0):
-    from cubicweb.server import schemaserial
-    schemaserial.HAS_FULLTEXT_CONTAINER = False
-    session.set_shared_data('do-not-insert-is_instance_of', True)
-    add_attribute('CWRType', 'fulltext_container')
-    schemaserial.HAS_FULLTEXT_CONTAINER = True
+     from cubicweb.server import schemaserial
+     schemaserial.HAS_FULLTEXT_CONTAINER = False
+     session.set_shared_data('do-not-insert-is_instance_of', True)
+     add_attribute('CWRType', 'fulltext_container')
+     schemaserial.HAS_FULLTEXT_CONTAINER = True
 
 
 
 if applcubicwebversion < (2, 50, 0) and cubicwebversion >= (2, 50, 0):
-    session.set_shared_data('do-not-insert-is_instance_of', True)
-    add_relation_type('is_instance_of')
-    # fill the relation using an efficient sql query instead of using rql
-    sql('INSERT INTO is_instance_of_relation '
-        '  SELECT * from is_relation')
-    checkpoint()
-    session.set_shared_data('do-not-insert-is_instance_of', False)
+     session.set_shared_data('do-not-insert-is_instance_of', True)
+     add_relation_type('is_instance_of')
+     # fill the relation using an efficient sql query instead of using rql
+     sql('INSERT INTO is_instance_of_relation '
+         '  SELECT * from is_relation')
+     checkpoint()
+     session.set_shared_data('do-not-insert-is_instance_of', False)
 
 if applcubicwebversion < (2, 42, 0) and cubicwebversion >= (2, 42, 0):
-    sql('ALTER TABLE entities ADD COLUMN mtime TIMESTAMP')
-    sql('UPDATE entities SET mtime=CURRENT_TIMESTAMP')
-    sql('CREATE INDEX entities_mtime_idx ON entities(mtime)')
-    sql('''CREATE TABLE deleted_entities (
+     sql('ALTER TABLE entities ADD COLUMN mtime TIMESTAMP')
+     sql('UPDATE entities SET mtime=CURRENT_TIMESTAMP')
+     sql('CREATE INDEX entities_mtime_idx ON entities(mtime)')
+     sql('''CREATE TABLE deleted_entities (
   eid INTEGER PRIMARY KEY NOT NULL,
   type VARCHAR(64) NOT NULL,
   source VARCHAR(64) NOT NULL,
   dtime TIMESTAMP NOT NULL,
   extid VARCHAR(256)
 )''')
-    sql('CREATE INDEX deleted_entities_type_idx ON deleted_entities(type)')
-    sql('CREATE INDEX deleted_entities_dtime_idx ON deleted_entities(dtime)')
-    sql('CREATE INDEX deleted_entities_extid_idx ON deleted_entities(extid)')
-    checkpoint()
-
+     sql('CREATE INDEX deleted_entities_type_idx ON deleted_entities(type)')
+     sql('CREATE INDEX deleted_entities_dtime_idx ON deleted_entities(dtime)')
+     sql('CREATE INDEX deleted_entities_extid_idx ON deleted_entities(extid)')
+     checkpoint()
--- a/rset.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/rset.py	Fri Jul 31 14:25:30 2009 +0200
@@ -83,7 +83,8 @@
         try:
             return self._rsetactions[key]
         except KeyError:
-            actions = self.vreg.possible_vobjects('actions', self.req, self, **kwargs)
+            actions = self.vreg.possible_vobjects('actions', self.req,
+                                                  rset=self, **kwargs)
             self._rsetactions[key] = actions
             return actions
 
--- a/schema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -19,9 +19,10 @@
 
 from yams import BadSchemaDefinition, buildobjs as ybo
 from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema
-from yams.constraints import BaseConstraint, StaticVocabularyConstraint
-from yams.reader import (CONSTRAINTS, RelationFileReader, PyFileReader,
-                         SchemaLoader)
+from yams.constraints import (BaseConstraint, StaticVocabularyConstraint,
+                              FormatConstraint)
+from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
+                         obsolete as yobsolete, cleanup_sys_modules)
 
 from rql import parse, nodes, RQLSyntaxError, TypeResolverException
 
@@ -33,7 +34,19 @@
 schema.use_py_datetime()
 nodes.use_py_datetime()
 
-BASEGROUPS = ('managers', 'users', 'guests', 'owners')
+#  set of meta-relations available for every entity types
+META_RELATIONS_TYPES = set((
+    'owned_by', 'created_by', 'is', 'is_instance_of', 'identity',
+    'eid', 'creation_date', 'modification_date', 'has_text', 'cwuri',
+    ))
+
+#  set of entity and relation types used to build the schema
+SCHEMA_TYPES = set((
+    'CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
+    'CWConstraint', 'CWConstraintType', 'RQLExpression',
+    'relation_type', 'from_entity', 'to_entity',
+    'constrained_by', 'cstrtype',
+    ))
 
 _LOGGER = getLogger('cubicweb.schemaloader')
 
@@ -50,75 +63,6 @@
         etype = ETYPE_NAME_MAP[etype]
     return etype
 
-# monkey path yams.builder.RelationDefinition to support a new wildcard type '@'
-# corresponding to system entity (ie meta but not schema)
-def _actual_types(self, schema, etype):
-    # two bits of error checking & reporting :
-    if type(etype) not in (str, list, tuple):
-        raise RuntimeError, ('Entity types must not be instances but strings or'
-                             ' list/tuples thereof. Ex. (bad, good) : '
-                             'SubjectRelation(Foo), SubjectRelation("Foo"). '
-                             'Hence, %r is not acceptable.' % etype)
-    # real work :
-    if etype == '**':
-        return self._pow_etypes(schema)
-    if isinstance(etype, (tuple, list)):
-        return etype
-    if '*' in etype or '@' in etype:
-        assert len(etype) in (1, 2)
-        etypes = ()
-        if '*' in etype:
-            etypes += tuple(self._wildcard_etypes(schema))
-        if '@' in etype:
-            etypes += tuple(system_etypes(schema))
-        return etypes
-    return (etype,)
-ybo.RelationDefinition._actual_types = _actual_types
-
-
-## cubicweb provides a RichString class for convenience
-class RichString(ybo.String):
-    """Convenience RichString attribute type
-    The following declaration::
-
-      class Card(EntityType):
-          content = RichString(fulltextindexed=True, default_format='text/rest')
-
-    is equivalent to::
-
-      class Card(EntityType):
-          content_format = String(meta=True, internationalizable=True,
-                                 default='text/rest', constraints=[format_constraint])
-          content  = String(fulltextindexed=True)
-    """
-    def __init__(self, default_format='text/plain', format_constraints=None, **kwargs):
-        self.default_format = default_format
-        self.format_constraints = format_constraints or [format_constraint]
-        super(RichString, self).__init__(**kwargs)
-
-PyFileReader.context['RichString'] = RichString
-
-## need to monkeypatch yams' _add_relation function to handle RichString
-yams_add_relation = ybo._add_relation
-@monkeypatch(ybo)
-def _add_relation(relations, rdef, name=None, insertidx=None):
-    if isinstance(rdef, RichString):
-        format_attrdef = ybo.String(meta=True, internationalizable=True,
-                                    default=rdef.default_format, maxsize=50,
-                                    constraints=rdef.format_constraints)
-        yams_add_relation(relations, format_attrdef, name+'_format', insertidx)
-    yams_add_relation(relations, rdef, name, insertidx)
-
-
-yams_EntityType_add_relation = ybo.EntityType.add_relation
-@monkeypatch(ybo.EntityType)
-def add_relation(self, rdef, name=None):
-    yams_EntityType_add_relation(self, rdef, name)
-    if isinstance(rdef, RichString) and not rdef in self._defined:
-        format_attr_name = (name or rdef.name) + '_format'
-        rdef = self.get_relations(format_attr_name).next()
-        self._ensure_relation_type(rdef)
-
 def display_name(req, key, form=''):
     """return a internationalized string for the key (schema entity or relation
     name) in a given form
@@ -253,7 +197,7 @@
     """return system entity types only: skip final, schema and application entities
     """
     for eschema in schema.entities():
-        if eschema.is_final() or eschema.schema_entity() or not eschema.meta:
+        if eschema.is_final() or eschema.schema_entity():
             continue
         yield eschema.type
 
@@ -293,6 +237,15 @@
                 continue
             yield rschema, attrschema
 
+    def main_attribute(self):
+        """convenience method that returns the *main* (i.e. the first non meta)
+        attribute defined in the entity schema
+        """
+        for rschema, _ in self.attribute_definitions():
+            if not (rschema in META_RELATIONS_TYPES
+                    or self.is_metadata(rschema)):
+                return rschema
+
     def add_subject_relation(self, rschema):
         """register the relation schema as possible subject relation"""
         super(CubicWebEntitySchema, self).add_subject_relation(rschema)
@@ -332,7 +285,7 @@
 
     def schema_entity(self):
         """return True if this entity type is used to build the schema"""
-        return self.type in self.schema.schema_entity_types()
+        return self.type in SCHEMA_TYPES
 
     def check_perm(self, session, action, eid=None):
         # NB: session may be a server session or a request object
@@ -370,6 +323,9 @@
             eid = getattr(rdef, 'eid', None)
         self.eid = eid
 
+    @property
+    def meta(self):
+        return self.type in META_RELATIONS_TYPES
 
     def update(self, subjschema, objschema, rdef):
         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
@@ -408,8 +364,8 @@
                (target == 'object' and card[1])
 
     def schema_relation(self):
-        return self.type in ('relation_type', 'from_entity', 'to_entity',
-                             'constrained_by', 'cstrtype')
+        """return True if this relation type is used to build the schema"""
+        return self.type in SCHEMA_TYPES
 
     def physical_mode(self):
         """return an appropriate mode for physical storage of this relation type:
@@ -446,7 +402,7 @@
 
 
     :type name: str
-    :ivar name: name of the schema, usually the application identifier
+    :ivar name: name of the schema, usually the instance identifier
 
     :type base: str
     :ivar base: path of the directory where the schema is defined
@@ -459,24 +415,16 @@
         self._eid_index = {}
         super(CubicWebSchema, self).__init__(*args, **kwargs)
         ybo.register_base_types(self)
-        rschema = self.add_relation_type(ybo.RelationType('eid', meta=True))
+        rschema = self.add_relation_type(ybo.RelationType('eid'))
         rschema.final = True
         rschema.set_default_groups()
-        rschema = self.add_relation_type(ybo.RelationType('has_text', meta=True))
+        rschema = self.add_relation_type(ybo.RelationType('has_text'))
         rschema.final = True
         rschema.set_default_groups()
-        rschema = self.add_relation_type(ybo.RelationType('identity', meta=True))
+        rschema = self.add_relation_type(ybo.RelationType('identity'))
         rschema.final = False
         rschema.set_default_groups()
 
-    def schema_entity_types(self):
-        """return the list of entity types used to build the schema"""
-        return frozenset(('CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
-                          'CWConstraint', 'CWConstraintType', 'RQLExpression',
-                          # XXX those are not really "schema" entity types
-                          #     but we usually don't want them as @* targets
-                          'CWProperty', 'CWPermission', 'State', 'Transition'))
-
     def add_entity_type(self, edef):
         edef.name = edef.name.encode()
         edef.name = bw_normalize_etype(edef.name)
@@ -799,7 +747,7 @@
             return self._check(session, x=eid)
         return self._check(session)
 
-PyFileReader.context['ERQLExpression'] = ERQLExpression
+PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression)
 
 class RRQLExpression(RQLExpression):
     def __init__(self, expression, mainvars=None, eid=None):
@@ -843,9 +791,10 @@
             kwargs['o'] = toeid
         return self._check(session, **kwargs)
 
-PyFileReader.context['RRQLExpression'] = RRQLExpression
+PyFileReader.context['RRQLExpression'] = yobsolete(RRQLExpression)
 
 # workflow extensions #########################################################
+from yams.buildobjs import _add_relation as yams_add_relation
 
 class workflowable_definition(ybo.metadefinition):
     """extends default EntityType's metaclass to add workflow relations
@@ -875,23 +824,6 @@
 
 # schema loading ##############################################################
 
-class CubicWebRelationFileReader(RelationFileReader):
-    """cubicweb specific relation file reader, handling additional RQL
-    constraints on a relation definition
-    """
-
-    def handle_constraint(self, rdef, constraint_text):
-        """arbitrary constraint is an rql expression for cubicweb"""
-        if not rdef.constraints:
-            rdef.constraints = []
-        rdef.constraints.append(RQLVocabularyConstraint(constraint_text))
-
-    def process_properties(self, rdef, relation_def):
-        if 'inline' in relation_def:
-            rdef.inlined = True
-        RelationFileReader.process_properties(self, rdef, relation_def)
-
-
 CONSTRAINTS['RQLConstraint'] = RQLConstraint
 CONSTRAINTS['RQLUniqueConstraint'] = RQLUniqueConstraint
 CONSTRAINTS['RQLVocabularyConstraint'] = RQLVocabularyConstraint
@@ -903,8 +835,6 @@
     the persistent schema
     """
     schemacls = CubicWebSchema
-    SchemaLoader.file_handlers.update({'.rel' : CubicWebRelationFileReader,
-                                       })
 
     def load(self, config, path=(), **kwargs):
         """return a Schema instance from the schema definition read
@@ -927,7 +857,7 @@
 
 class CubicWebSchemaLoader(BootstrapSchemaLoader):
     """cubicweb specific schema loader, automatically adding metadata to the
-    application's schema
+    instance's schema
     """
 
     def load(self, config, **kwargs):
@@ -939,7 +869,11 @@
             path = reversed([config.apphome] + config.cubes_path())
         else:
             path = reversed(config.cubes_path())
-        return super(CubicWebSchemaLoader, self).load(config, path=path, **kwargs)
+        try:
+            return super(CubicWebSchemaLoader, self).load(config, path=path, **kwargs)
+        finally:
+            # we've to cleanup modules imported from cubicweb.schemas as well
+            cleanup_sys_modules([self.lib_directory])
 
     def _load_definition_files(self, cubes):
         for filepath in (join(self.lib_directory, 'bootstrap.py'),
@@ -954,49 +888,22 @@
                 self.handle_file(filepath)
 
 
+set_log_methods(CubicWebSchemaLoader, getLogger('cubicweb.schemaloader'))
+set_log_methods(BootstrapSchemaLoader, getLogger('cubicweb.bootstrapschemaloader'))
+set_log_methods(RQLExpression, getLogger('cubicweb.schema'))
+
 # _() is just there to add messages to the catalog, don't care about actual
 # translation
 PERM_USE_TEMPLATE_FORMAT = _('use_template_format')
-
-class FormatConstraint(StaticVocabularyConstraint):
-    need_perm_formats = [_('text/cubicweb-page-template')]
-
-    regular_formats = (_('text/rest'),
-                       _('text/html'),
-                       _('text/plain'),
-                       )
-    def __init__(self):
-        pass
-
-    def serialize(self):
-        """called to make persistent valuable data of a constraint"""
-        return None
+NEED_PERM_FORMATS = [_('text/cubicweb-page-template')]
 
-    @classmethod
-    def deserialize(cls, value):
-        """called to restore serialized data of a constraint. Should return
-        a `cls` instance
-        """
-        return cls()
-
-    def vocabulary(self, entity=None, req=None):
-        if req is None and entity is not None:
-            req = entity.req
-        if req is not None and req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
-            return self.regular_formats + tuple(self.need_perm_formats)
-        return self.regular_formats
-
-    def __str__(self):
-        return 'value in (%s)' % u', '.join(repr(unicode(word)) for word in self.vocabulary())
-
-
-format_constraint = FormatConstraint()
-CONSTRAINTS['FormatConstraint'] = FormatConstraint
-PyFileReader.context['format_constraint'] = format_constraint
-
-set_log_methods(CubicWebSchemaLoader, getLogger('cubicweb.schemaloader'))
-set_log_methods(BootstrapSchemaLoader, getLogger('cubicweb.bootstrapschemaloader'))
-set_log_methods(RQLExpression, getLogger('cubicweb.schema'))
+@monkeypatch(FormatConstraint)
+def vocabulary(self, entity=None, req=None):
+    if req is None and entity is not None:
+        req = entity.req
+    if req is not None and req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
+        return self.regular_formats + tuple(NEED_PERM_FORMATS)
+    return self.regular_formats
 
 # XXX monkey patch PyFileReader.import_erschema until bw_normalize_etype is
 # necessary
--- a/schemas/Bookmark.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schemas/Bookmark.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,9 +5,21 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
+__docformat__ = "restructuredtext en"
+_ = unicode
 
-class Bookmark(MetaUserEntityType):
+from yams.buildobjs import EntityType, RelationType, SubjectRelation, String
+from cubicweb.schema import RRQLExpression
+
+class Bookmark(EntityType):
     """bookmarks are used to have user's specific internal links"""
+    permissions = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers', 'users',),
+        'delete': ('managers', 'owners',),
+        'update': ('managers', 'owners',),
+        }
+
     title = String(required=True, maxsize=128, internationalizable=True)
     path  = String(maxsize=512, required=True,
                    description=_("relative url of the bookmarked page"))
@@ -16,7 +28,7 @@
                                     description=_("users using this bookmark"))
 
 
-class bookmarked_by(MetaUserRelationType):
+class bookmarked_by(RelationType):
     permissions = {'read':   ('managers', 'users', 'guests',),
                    # test user in users group to avoid granting permission to anonymous user
                    'add':    ('managers', RRQLExpression('O identity U, U in_group G, G name "users"')),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schemas/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,24 @@
+# permissions for "meta" entity type (readable by anyone, can only be
+# added/deleted by managers)
+META_ETYPE_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    ('managers',),
+    'delete': ('managers',),
+    'update': ('managers', 'owners',),
+    }
+
+# permissions for "meta" relation type (readable by anyone, can only be
+# added/deleted by managers)
+META_RTYPE_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    ('managers',),
+    'delete': ('managers',),
+    }
+
+# permissions for relation type that should only set by hooks using unsafe
+# execute, readable by anyone
+HOOKS_RTYPE_PERMS = {
+    'read':   ('managers', 'users', 'guests',),
+    'add':    (),
+    'delete': (),
+    }
--- a/schemas/base.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schemas/base.py	Fri Jul 31 14:25:30 2009 +0200
@@ -6,11 +6,16 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
+from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
+                            String, Boolean, Datetime, Password)
+from cubicweb.schema import (RQLConstraint, WorkflowableEntityType,
+                             ERQLExpression, RRQLExpression)
+from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS
 
 class CWUser(WorkflowableEntityType):
     """define a CubicWeb user"""
-    meta = True # XXX backported from old times, shouldn't be there anymore
     permissions = {
         'read':   ('managers', 'users', ERQLExpression('X identity U')),
         'add':    ('managers',),
@@ -35,7 +40,7 @@
                                description=_('groups grant permissions to the user'))
 
 
-class EmailAddress(MetaEntityType):
+class EmailAddress(EntityType):
     """an electronic mail address associated to a short alias"""
     permissions = {
         'read':   ('managers', 'users', 'guests',), # XXX if P use_email X, U has_read_permission P
@@ -81,11 +86,11 @@
         'delete': ('managers', RRQLExpression('U has_update_permission S'),),
         }
 
-class in_group(MetaRelationType):
+class in_group(RelationType):
     """core relation indicating a user's groups"""
-    meta = False
+    permissions = META_RTYPE_PERMS
 
-class owned_by(MetaRelationType):
+class owned_by(RelationType):
     """core relation indicating owners of an entity. This relation
     implicitly put the owner into the owners group for the entity
     """
@@ -97,10 +102,10 @@
     # 0..n cardinality for entities created by internal session (no attached user)
     # and to support later deletion of a user which has created some entities
     cardinality = '**'
-    subject = '**'
+    subject = '*'
     object = 'CWUser'
 
-class created_by(MetaRelationType):
+class created_by(RelationType):
     """core relation indicating the original creator of an entity"""
     permissions = {
         'read':   ('managers', 'users', 'guests'),
@@ -110,22 +115,28 @@
     # 0..1 cardinality for entities created by internal session (no attached user)
     # and to support later deletion of a user which has created some entities
     cardinality = '?*'
-    subject = '**'
+    subject = '*'
     object = 'CWUser'
 
 
-class creation_date(MetaAttributeRelationType):
+class creation_date(RelationType):
     """creation time of an entity"""
     cardinality = '11'
-    subject = '**'
+    subject = '*'
     object = 'Datetime'
 
-class modification_date(MetaAttributeRelationType):
+class modification_date(RelationType):
     """latest modification time of an entity"""
     cardinality = '11'
-    subject = '**'
+    subject = '*'
     object = 'Datetime'
 
+class cwuri(RelationType):
+    """internal entity uri"""
+    cardinality = '11'
+    subject = '*'
+    object = 'String'
+
 
 class CWProperty(EntityType):
     """used for cubicweb configuration. Once a property has been created you
@@ -137,7 +148,6 @@
         'update': ('managers', 'owners',),
         'delete': ('managers', 'owners',),
         }
-    meta = True
     # key is a reserved word for mysql
     pkey = String(required=True, internationalizable=True, maxsize=256,
                   description=_('defines what\'s the property is applied for. '
@@ -152,7 +162,7 @@
                                              ' a global property'))
 
 
-class for_user(MetaRelationType):
+class for_user(RelationType):
     """link a property to the user which want this property customization. Unless
     you're a site manager, this relation will be handled automatically.
     """
@@ -164,9 +174,11 @@
     inlined = True
 
 
-class CWPermission(MetaEntityType):
+class CWPermission(EntityType):
     """entity type that may be used to construct some advanced security configuration
     """
+    permissions = META_ETYPE_PERMS
+
     name = String(required=True, indexed=True, internationalizable=True, maxsize=100,
                   description=_('name or identifier of the permission'))
     label = String(required=True, internationalizable=True, maxsize=100,
@@ -186,7 +198,7 @@
         'delete': ('managers',),
         }
 
-class require_group(MetaRelationType):
+class require_group(RelationType):
     """used to grant a permission to a group"""
     permissions = {
         'read':   ('managers', 'users', 'guests'),
@@ -199,8 +211,31 @@
     """generic relation to link one entity to another"""
     symetric = True
 
+class ExternalUri(EntityType):
+    """a URI representing an object in external data store"""
+    uri = String(required=True, unique=True, maxsize=256,
+                 description=_('the URI of the object'))
 
-class CWCache(MetaEntityType):
+class same_as(RelationType):
+    """generic relation to specify that an external entity represent the same
+    object as a local one:
+       http://www.w3.org/TR/owl-ref/#sameAs-def
+
+    NOTE: You'll have to explicitly declare which entity types can have a
+    same_as relation
+    """
+    permissions = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers', 'users'),
+        'delete': ('managers', 'owners'),
+        }
+    cardinality = '*1'
+    symetric = True
+    # NOTE: the 'object = ExternalUri' declaration will still be mandatory
+    #       in the cube's schema.
+    object = 'ExternalUri'
+
+class CWCache(EntityType):
     """a simple cache entity characterized by a name and
     a validity date.
 
@@ -212,7 +247,7 @@
     permissions = {
         'read':   ('managers', 'users', 'guests'),
         'add':    ('managers',),
-        'update': ('managers', 'users',),
+        'update': ('managers', 'users',), # XXX
         'delete': ('managers',),
         }
 
--- a/schemas/bootstrap.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schemas/bootstrap.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,36 +1,38 @@
-"""core CubicWeb schema necessary for bootstrapping the actual application's schema
+"""core CubicWeb schema necessary for bootstrapping the actual instance's schema
 
 :organization: Logilab
 :copyright: 2001-2009 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
 """
+__docformat__ = "restructuredtext en"
+_ = unicode
 
-from cubicweb.schema import format_constraint
-
+from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
+                            ObjectRelation, RichString, String, Boolean, Int)
+from cubicweb.schema import RQLConstraint
+from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS
 
 # not restricted since as "is" is handled as other relations, guests need
 # access to this
-class CWEType(MetaEntityType):
-    """define an entity type, used to build the application schema"""
+class CWEType(EntityType):
+    """define an entity type, used to build the instance schema"""
+    permissions = META_ETYPE_PERMS
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
     description = RichString(internationalizable=True,
                              description=_('semantic description of this entity type'))
-    meta = Boolean(description=_('is it an application entity type or not ?'))
     # necessary to filter using RQL
     final = Boolean(description=_('automatic'))
 
 
-class CWRType(MetaEntityType):
-    """define a relation type, used to build the application schema"""
+class CWRType(EntityType):
+    """define a relation type, used to build the instance schema"""
+    permissions = META_ETYPE_PERMS
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/plain', constraints=[format_constraint])
-    description = String(internationalizable=True,
-                         description=_('semantic description of this relation type'))
-    meta = Boolean(description=_('is it an application relation type or not ?'))
+    description = RichString(internationalizable=True,
+                             description=_('semantic description of this relation type'))
     symetric = Boolean(description=_('is this relation equivalent in both direction ?'))
     inlined = Boolean(description=_('is this relation physically inlined? you should know what you\'re doing if you are changing this!'))
     fulltext_container = String(description=_('if full text content of subject/object entity '
@@ -40,12 +42,13 @@
     final = Boolean(description=_('automatic'))
 
 
-class CWAttribute(MetaEntityType):
+class CWAttribute(EntityType):
     """define a final relation: link a final relation type from a non final
     entity to a final entity type.
 
-    used to build the application schema
+    used to build the instance schema
     """
+    permissions = META_ETYPE_PERMS
     relation_type = SubjectRelation('CWRType', cardinality='1*',
                                     constraints=[RQLConstraint('O final TRUE')],
                                     composite='object')
@@ -67,10 +70,8 @@
     internationalizable = Boolean(description=_('is this attribute\'s value translatable'))
     defaultval = String(maxsize=256)
 
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/plain', constraints=[format_constraint])
-    description = String(internationalizable=True,
-                         description=_('semantic description of this attribute'))
+    description = RichString(internationalizable=True,
+                             description=_('semantic description of this attribute'))
 
 
 CARDINALITY_VOCAB = [_('?*'), _('1*'), _('+*'), _('**'),
@@ -78,12 +79,13 @@
                      _('?1'), _('11'), _('+1'), _('*1'),
                      _('??'), _('1?'), _('+?'), _('*?')]
 
-class CWRelation(MetaEntityType):
+class CWRelation(EntityType):
     """define a non final relation: link a non final relation type from a non
     final entity to a non final entity type.
 
-    used to build the application schema
+    used to build the instance schema
     """
+    permissions = META_ETYPE_PERMS
     relation_type = SubjectRelation('CWRType', cardinality='1*',
                                     constraints=[RQLConstraint('O final FALSE')],
                                     composite='object')
@@ -107,15 +109,14 @@
                        vocabulary=('', _('subject'), _('object')),
                        maxsize=8, default=None)
 
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/plain', constraints=[format_constraint])
-    description = String(internationalizable=True,
-                         description=_('semantic description of this relation'))
+    description = RichString(internationalizable=True,
+                             description=_('semantic description of this relation'))
 
 
 # not restricted since it has to be read when checking allowed transitions
-class RQLExpression(MetaEntityType):
+class RQLExpression(EntityType):
     """define a rql expression used to define permissions"""
+    permissions = META_ETYPE_PERMS
     exprtype = String(required=True, vocabulary=['ERQLExpression', 'RRQLExpression'])
     mainvars = String(maxsize=8,
                       description=_('name of the main variables which should be '
@@ -140,21 +141,24 @@
                                         description=_('rql expression allowing to update entities of this type'))
 
 
-class CWConstraint(MetaEntityType):
+class CWConstraint(EntityType):
     """define a schema constraint"""
+    permissions = META_ETYPE_PERMS
     cstrtype = SubjectRelation('CWConstraintType', cardinality='1*')
     value = String(description=_('depends on the constraint type'))
 
 
-class CWConstraintType(MetaEntityType):
+class CWConstraintType(EntityType):
     """define a schema constraint type"""
+    permissions = META_ETYPE_PERMS
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
 
 
 # not restricted since it has to be read when checking allowed transitions
-class CWGroup(MetaEntityType):
+class CWGroup(EntityType):
     """define a CubicWeb users group"""
+    permissions = META_ETYPE_PERMS
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
 
@@ -169,40 +173,55 @@
 
 
 
-class relation_type(MetaRelationType):
+class relation_type(RelationType):
     """link a relation definition to its relation type"""
-    inlined = True
-class from_entity(MetaRelationType):
-    """link a relation definition to its subject entity type"""
+    permissions = META_RTYPE_PERMS
     inlined = True
-class to_entity(MetaRelationType):
-    """link a relation definition to its object entity type"""
-    inlined = True
-class constrained_by(MetaRelationType):
-    """constraints applying on this relation"""
 
-class cstrtype(MetaRelationType):
-    """constraint factory"""
+class from_entity(RelationType):
+    """link a relation definition to its subject entity type"""
+    permissions = META_RTYPE_PERMS
     inlined = True
 
-class read_permission(MetaRelationType):
+class to_entity(RelationType):
+    """link a relation definition to its object entity type"""
+    permissions = META_RTYPE_PERMS
+    inlined = True
+
+class constrained_by(RelationType):
+    """constraints applying on this relation"""
+    permissions = META_RTYPE_PERMS
+
+class cstrtype(RelationType):
+    """constraint factory"""
+    permissions = META_RTYPE_PERMS
+    inlined = True
+
+class read_permission(RelationType):
     """core relation giving to a group the permission to read an entity or
     relation type
     """
-class add_permission(MetaRelationType):
+    permissions = META_RTYPE_PERMS
+
+class add_permission(RelationType):
     """core relation giving to a group the permission to add an entity or
     relation type
     """
-class delete_permission(MetaRelationType):
+    permissions = META_RTYPE_PERMS
+
+class delete_permission(RelationType):
     """core relation giving to a group the permission to delete an entity or
     relation type
     """
-class update_permission(MetaRelationType):
+    permissions = META_RTYPE_PERMS
+
+class update_permission(RelationType):
     """core relation giving to a group the permission to update an entity type
     """
+    permissions = META_RTYPE_PERMS
 
 
-class is_(MetaRelationType):
+class is_(RelationType):
     """core relation indicating the type of an entity
     """
     name = 'is'
@@ -214,10 +233,10 @@
         'delete': (),
         }
     cardinality = '1*'
-    subject = '**'
+    subject = '*'
     object = 'CWEType'
 
-class is_instance_of(MetaRelationType):
+class is_instance_of(RelationType):
     """core relation indicating the types (including specialized types)
     of an entity
     """
@@ -229,10 +248,10 @@
         'delete': (),
         }
     cardinality = '+*'
-    subject = '**'
+    subject = '*'
     object = 'CWEType'
 
-class specializes(MetaRelationType):
+class specializes(RelationType):
     name = 'specializes'
     permissions = {
         'read':   ('managers', 'users', 'guests'),
--- a/schemas/workflow.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schemas/workflow.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,11 +5,20 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
+__docformat__ = "restructuredtext en"
+_ = unicode
 
-class State(MetaEntityType):
+from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
+                            ObjectRelation, RichString, String)
+from cubicweb.schema import RQLConstraint
+from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS, HOOKS_RTYPE_PERMS
+
+class State(EntityType):
     """used to associate simple states to an entity type and/or to define
     workflows
     """
+    permissions = META_ETYPE_PERMS
+
     name = String(required=True, indexed=True, internationalizable=True,
                   maxsize=256)
     description = RichString(fulltextindexed=True, default_format='text/rest',
@@ -28,15 +37,15 @@
                                    description=_('initial state for entities of this type'))
 
 
-class Transition(MetaEntityType):
+class Transition(EntityType):
     """use to define a transition from one or multiple states to a destination
     states in workflow's definitions.
     """
+    permissions = META_ETYPE_PERMS
+
     name = String(required=True, indexed=True, internationalizable=True,
                   maxsize=256)
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/rest', constraints=[format_constraint])
-    description = String(fulltextindexed=True,
+    description = RichString(fulltextindexed=True,
                          description=_('semantic description of this transition'))
     condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject',
                                 description=_('a RQL expression which should return some results, '
@@ -56,20 +65,23 @@
                                         description=_('destination state for this transition'))
 
 
-class TrInfo(MetaEntityType):
+class TrInfo(EntityType):
+    permissions = META_ETYPE_PERMS
+
     from_state = SubjectRelation('State', cardinality='?*')
     to_state = SubjectRelation('State', cardinality='1*')
-    comment_format = String(meta=True, internationalizable=True, maxsize=50,
-                            default='text/rest', constraints=[format_constraint])
-    comment = String(fulltextindexed=True)
+    comment = RichString(fulltextindexed=True)
     # get actor and date time using owned_by and creation_date
 
 
-class from_state(MetaRelationType):
+class from_state(RelationType):
+    permissions = HOOKS_RTYPE_PERMS
     inlined = True
-class to_state(MetaRelationType):
+class to_state(RelationType):
+    permissions = HOOKS_RTYPE_PERMS
     inlined = True
-class wf_info_for(MetaRelationType):
+
+class wf_info_for(RelationType):
     """link a transition information to its object"""
     permissions = {
         'read':   ('managers', 'users', 'guests',),# RRQLExpression('U has_read_permission O')),
@@ -80,30 +92,39 @@
     composite = 'object'
     fulltext_container = composite
 
-class state_of(MetaRelationType):
+class state_of(RelationType):
     """link a state to one or more entity type"""
-class transition_of(MetaRelationType):
+    permissions = META_RTYPE_PERMS
+class transition_of(RelationType):
     """link a transition to one or more entity type"""
+    permissions = META_RTYPE_PERMS
 
-class initial_state(MetaRelationType):
+class initial_state(RelationType):
     """indicate which state should be used by default when an entity using
     states is created
     """
-    inlined = True
-
-class destination_state(MetaRelationType):
-    """destination state of a transition"""
+    permissions = META_RTYPE_PERMS
     inlined = True
 
-class allowed_transition(MetaRelationType):
-    """allowed transition from this state"""
+class destination_state(RelationType):
+    """destination state of a transition"""
+    permissions = META_RTYPE_PERMS
+    inlined = True
 
-class in_state(UserRelationType):
+class allowed_transition(RelationType):
+    """allowed transition from this state"""
+    permissions = META_RTYPE_PERMS
+
+class in_state(RelationType):
     """indicate the current state of an entity"""
-    meta = True
     # not inlined intentionnaly since when using ldap sources, user'state
     # has to be stored outside the CWUser table
 
     # add/delete perms given to managers/users, after what most of the job
     # is done by workflow enforcment
+    permissions = {
+        'read':   ('managers', 'users', 'guests',),
+        'add':    ('managers', 'users',), # XXX has_update_perm
+        'delete': ('managers', 'users',),
+        }
 
--- a/schemaviewer.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/schemaviewer.py	Fri Jul 31 14:25:30 2009 +0200
@@ -6,11 +6,11 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.common.ureports import Section, Title, Table, Link, Span, Text
 from yams.schema2dot import CARD_MAP
 
-_ = unicode
 I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')]
 
 class SchemaViewer(object):
@@ -38,8 +38,7 @@
                        klass='acl')
 
 
-    def visit_schema(self, schema, display_relations=0,
-                     skiprels=(), skipmeta=True):
+    def visit_schema(self, schema, display_relations=0, skiptypes=()):
         """get a layout for a whole schema"""
         title = Title(self.req._('Schema %s') % schema.name,
                       klass='titleUnderline')
@@ -48,21 +47,15 @@
                                            klass='titleUnderline'),))
         layout.append(esection)
         eschemas = [eschema for eschema in schema.entities()
-                    if not eschema.is_final()]
-        if skipmeta:
-            eschemas = [eschema for eschema in eschemas
-                        if not eschema.meta]
+                    if not (eschema.is_final() or eschema in skiptypes)]
         for eschema in sorted(eschemas):
-            esection.append(self.visit_entityschema(eschema, skiprels))
+            esection.append(self.visit_entityschema(eschema, skiptypes))
         if display_relations:
             title = Title(self.req._('Relations'), klass='titleUnderline')
             rsection = Section(children=(title,))
             layout.append(rsection)
             relations = [rschema for rschema in schema.relations()
-                         if not (rschema.is_final() or rschema.type in skiprels)]
-            if skipmeta:
-                relations = [rschema for rschema in relations
-                             if not rschema.meta]
+                         if not (rschema.is_final() or rschema.type in skiptypes)]
             keys = [(rschema.type, rschema) for rschema in relations]
             for key, rschema in sorted(keys):
                 relstr = self.visit_relationschema(rschema)
@@ -107,17 +100,13 @@
     def stereotype(self, name):
         return Span((' <<%s>>' % name,), klass='stereotype')
 
-    def visit_entityschema(self, eschema, skiprels=()):
+    def visit_entityschema(self, eschema, skiptypes=()):
         """get a layout for an entity schema"""
         etype = eschema.type
         layout = Section(children=' ', klass='clear')
         layout.append(Link(etype,'&nbsp;' , id=etype)) # anchor
         title = Link(self.eschema_link_url(eschema), etype)
-        if eschema.meta:
-            stereotype = self.stereotype('meta')
-            boxchild = [Section(children=(title, ' (%s)'%eschema.display_name(self.req), stereotype), klass='title')]
-        else:
-            boxchild = [Section(children=(title, ' (%s)'%eschema.display_name(self.req)), klass='title')]
+        boxchild = [Section(children=(title, ' (%s)'% eschema.display_name(self.req)), klass='title')]
         table = Table(cols=4, rheaders=1,
                       children=self._entity_attributes_data(eschema))
         boxchild.append(Section(children=(table,), klass='body'))
@@ -129,7 +118,7 @@
         rels = []
         first = True
         for rschema, targetschemas, x in eschema.relation_definitions():
-            if rschema.type in skiprels:
+            if rschema.type in skiptypes:
                 continue
             if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
                 continue
--- a/selectors.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/selectors.py	Fri Jul 31 14:25:30 2009 +0200
@@ -287,7 +287,7 @@
 
 @objectify_selector
 @lltrace
-def none_rset(cls, req, rset=None, *args, **kwargs):
+def none_rset(cls, req, rset=None, **kwargs):
     """accept no result set (e.g. given rset is None)"""
     if rset is None:
         return 1
@@ -295,7 +295,7 @@
 
 @objectify_selector
 @lltrace
-def any_rset(cls, req, rset=None, *args, **kwargs):
+def any_rset(cls, req, rset=None, **kwargs):
     """accept result set, whatever the number of result it contains"""
     if rset is not None:
         return 1
@@ -303,7 +303,7 @@
 
 @objectify_selector
 @lltrace
-def nonempty_rset(cls, req, rset=None, *args, **kwargs):
+def nonempty_rset(cls, req, rset=None, **kwargs):
     """accept any non empty result set"""
     if rset is not None and rset.rowcount:
         return 1
@@ -311,7 +311,7 @@
 
 @objectify_selector
 @lltrace
-def empty_rset(cls, req, rset=None, *args, **kwargs):
+def empty_rset(cls, req, rset=None, **kwargs):
     """accept empty result set"""
     if rset is not None and rset.rowcount == 0:
         return 1
@@ -319,7 +319,7 @@
 
 @objectify_selector
 @lltrace
-def one_line_rset(cls, req, rset=None, row=None, *args, **kwargs):
+def one_line_rset(cls, req, rset=None, row=None, **kwargs):
     """if row is specified, accept result set with a single line of result,
     else accepts anyway
     """
@@ -329,7 +329,7 @@
 
 @objectify_selector
 @lltrace
-def two_lines_rset(cls, req, rset=None, *args, **kwargs):
+def two_lines_rset(cls, req, rset=None, **kwargs):
     """accept result set with *at least* two lines of result"""
     if rset is not None and rset.rowcount > 1:
         return 1
@@ -337,7 +337,7 @@
 
 @objectify_selector
 @lltrace
-def two_cols_rset(cls, req, rset=None, *args, **kwargs):
+def two_cols_rset(cls, req, rset=None, **kwargs):
     """accept result set with at least one line and two columns of result"""
     if rset is not None and rset.rowcount and len(rset.rows[0]) > 1:
         return 1
@@ -345,7 +345,7 @@
 
 @objectify_selector
 @lltrace
-def paginated_rset(cls, req, rset=None, *args, **kwargs):
+def paginated_rset(cls, req, rset=None, **kwargs):
     """accept result set with more lines than the page size.
 
     Page size is searched in (respecting order):
@@ -366,7 +366,7 @@
 
 @objectify_selector
 @lltrace
-def sorted_rset(cls, req, rset=None, row=None, col=0, **kwargs):
+def sorted_rset(cls, req, rset=None, **kwargs):
     """accept sorted result set"""
     rqlst = rset.syntax_tree()
     if len(rqlst.children) > 1 or not rqlst.children[0].orderby:
@@ -375,7 +375,7 @@
 
 @objectify_selector
 @lltrace
-def one_etype_rset(cls, req, rset=None, row=None, col=0, *args, **kwargs):
+def one_etype_rset(cls, req, rset=None, col=0, **kwargs):
     """accept result set where entities in the specified column (or 0) are all
     of the same type
     """
@@ -387,7 +387,7 @@
 
 @objectify_selector
 @lltrace
-def two_etypes_rset(cls, req, rset=None, row=None, col=0, **kwargs):
+def two_etypes_rset(cls, req, rset=None, col=0, **kwargs):
     """accept result set where entities in the specified column (or 0) are not
     of the same type
     """
@@ -572,7 +572,7 @@
 
     def __call__(self, cls, req, rset=None, *args, **kwargs):
         try:
-            cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs)
+            cls.vreg.select(self.registry, self.oid, req, rset=rset, **kwargs)
             return 1
         except NoSelectableObject:
             return 0
@@ -616,10 +616,10 @@
     @lltrace
     def __call__(self, cls, req, *args, **kwargs):
         try:
-            etype = req.form['etype']
+            etype = kwargs['etype']
         except KeyError:
             try:
-                etype = kwargs['etype']
+                etype = req.form['etype']
             except KeyError:
                 return 0
         else:
@@ -942,6 +942,7 @@
     def __repr__(self):
         return u'<rql_condition "%s" at %x>' % (self.rql, id(self))
 
+
 class but_etype(EntitySelector):
     """accept if the given entity types are not found in the result set.
 
--- a/server/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -24,16 +24,16 @@
     a initial user)
     """
     from glob import glob
-    from cubicweb.schema import BASEGROUPS
+    from yams import BASE_GROUPS
     from cubicweb.dbapi import in_memory_cnx
     from cubicweb.server.repository import Repository
     from cubicweb.server.utils import manager_userpasswd
     from cubicweb.server.sqlutils import sqlexec, sqlschema, sqldropschema
     # configuration to avoid db schema loading and user'state checking
     # on connection
-    read_application_schema = config.read_application_schema
+    read_instance_schema = config.read_instance_schema
     bootstrap_schema = config.bootstrap_schema
-    config.read_application_schema = False
+    config.read_instance_schema = False
     config.creating = True
     config.bootstrap_schema = True
     config.consider_user_state = False
@@ -92,7 +92,7 @@
         else:
             login, pwd = unicode(source['db-user']), source['db-password']
     print '-> inserting default user and default groups.'
-    for group in BASEGROUPS:
+    for group in BASE_GROUPS:
         rset = session.execute('INSERT CWGroup X: X name %(name)s',
                                {'name': unicode(group)})
     rset = session.execute('INSERT CWUser X: X login %(login)s, X upassword %(pwd)s',
@@ -136,11 +136,11 @@
     session.close()
     # restore initial configuration
     config.creating = False
-    config.read_application_schema = read_application_schema
+    config.read_instance_schema = read_instance_schema
     config.bootstrap_schema = bootstrap_schema
     config.consider_user_state = True
     config.set_language = True
-    print '-> database for application %s initialized.' % config.appid
+    print '-> database for instance %s initialized.' % config.appid
 
 
 def initialize_schema(config, schema, mhandler, event='create'):
@@ -152,7 +152,7 @@
     # execute cubes pre<event> script if any
     for path in reversed(paths):
         mhandler.exec_event_script('pre%s' % event, path)
-    # enter application'schema into the database
+    # enter instance'schema into the database
     serialize_schema(mhandler.rqlcursor, schema)
     # execute cubicweb's post<event> script
     mhandler.exec_event_script('post%s' % event)
--- a/server/checkintegrity.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/checkintegrity.py	Fri Jul 31 14:25:30 2009 +0200
@@ -282,7 +282,7 @@
 
 
 def check(repo, cnx, checks, reindex, fix):
-    """check integrity of application's repository,
+    """check integrity of instance's repository,
     using given user and password to locally connect to the repository
     (no running cubicweb server needed)
     """
--- a/server/hooks.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/hooks.py	Fri Jul 31 14:25:30 2009 +0200
@@ -33,6 +33,9 @@
         entity['creation_date'] = datetime.now()
     if not 'modification_date' in entity:
         entity['modification_date'] = datetime.now()
+    if not 'cwuri' in entity:
+        if not session.get_shared_data('do-not-insert-cwuri'):
+            entity['cwuri'] = session.base_url() + u'eid/%s' % entity.eid
 
 def setmtime_before_update_entity(session, entity):
     """update an entity -> set modification date"""
--- a/server/hooksmanager.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/hooksmanager.py	Fri Jul 31 14:25:30 2009 +0200
@@ -34,7 +34,8 @@
                   'before_delete_entity', 'after_delete_entity')
 RELATIONS_HOOKS = ('before_add_relation',   'after_add_relation' ,
                    'before_delete_relation','after_delete_relation')
-SYSTEM_HOOKS = ('server_startup', 'server_shutdown',
+SYSTEM_HOOKS = ('server_backup', 'server_restore',
+                'server_startup', 'server_shutdown',
                 'session_open', 'session_close')
 
 ALL_HOOKS = frozenset(ENTITIES_HOOKS + RELATIONS_HOOKS + SYSTEM_HOOKS)
@@ -65,10 +66,11 @@
     def register_hook(self, function, event, etype=''):
         """register a function to call when <event> occurs
 
-         <etype> is an entity/relation type or an empty string.
-         If etype is the empty string, the function will be called at each
-         event, else the function will be called only when event occurs on an
-         entity/relation of the given type.
+        <etype> is an entity/relation type or an empty string.
+
+        If etype is the empty string, the function will be called at each event,
+        else the function will be called only when event occurs on an entity or
+        relation of the given type.
         """
         assert event in ALL_HOOKS, '%r NOT IN %r' % (event, ALL_HOOKS)
         assert (not event in SYSTEM_HOOKS or not etype), (event, etype)
@@ -82,19 +84,28 @@
             self.error('can\'t register hook %s on %s (%s)',
                        event, etype or 'any', function.func_name)
 
-    def unregister_hook(self, function, event, etype=''):
-        """register a function to call when <event> occurs
-
-        <etype> is an entity/relation type or an empty string.
-        If etype is the empty string, the function will be called at each
-        event, else the function will be called only when event occurs on an
-        entity/relation of the given type.
+    def unregister_hook(self, function_or_cls, event=None, etype=''):
+        """unregister a function to call when <event> occurs, or a Hook subclass.
+        In the later case, event/type information are extracted from the given
+        class.
         """
-        assert event in ALL_HOOKS, event
-        etype = etype or ''
-        self.info('unregister hook %s on %s (%s)', event, etype,
-                  function.func_name)
-        self._hooks[event][etype].remove(function)
+        if isinstance(function_or_cls, type) and issubclass(function_or_cls, Hook):
+            for event, ertype in function_or_cls.register_to():
+                for hook in self._hooks[event][ertype]:
+                    if getattr(hook, 'im_self', None).__class__ is function_or_cls:
+                        self._hooks[event][ertype].remove(hook)
+                        self.info('unregister hook %s on %s (%s)', event, etype,
+                                  function_or_cls.__name__)
+                        break
+                else:
+                    self.warning("can't unregister hook %s on %s (%s), not found",
+                                 event, etype, function_or_cls.__name__)
+        else:
+            assert event in ALL_HOOKS, event
+            etype = etype or ''
+            self.info('unregister hook %s on %s (%s)', event, etype,
+                      function_or_cls.func_name)
+            self._hooks[event][etype].remove(function_or_cls)
 
     def call_hooks(self, __event, __type='', *args, **kwargs):
         """call hook matching event and optional type"""
@@ -220,9 +231,9 @@
                '%s: events is expected to be a tuple, not %s' % (
             cls, type(cls.events))
         for event in cls.events:
-            if event == 'server_startup':
+            if event in SYSTEM_HOOKS:
                 assert not cls.accepts or cls.accepts == ('Any',), \
-                       '%s doesnt make sense on server_startup' % cls.accepts
+                       '%s doesnt make sense on %s' % (cls.accepts, event)
                 cls.accepts = ('Any',)
             for ertype in cls.accepts:
                 if (event, ertype) in done:
--- a/server/migractions.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/migractions.py	Fri Jul 31 14:25:30 2009 +0200
@@ -109,61 +109,26 @@
 
     def backup_database(self, backupfile=None, askconfirm=True):
         config = self.config
-        source = config.sources()['system']
-        helper = get_adv_func_helper(source['db-driver'])
-        date = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
-        app = config.appid
-        backupfile = backupfile or join(config.backup_dir(),
-                                        '%s-%s.dump' % (app, date))
-        if exists(backupfile):
-            if not self.confirm('a backup already exists for %s, overwrite it?' % app):
-                return
-        elif askconfirm and not self.confirm('backup %s database?' % app):
-            return
-        cmd = helper.backup_command(source['db-name'], source.get('db-host'),
-                                    source.get('db-user'), backupfile,
-                                    keepownership=False)
-        while True:
-            print cmd
-            if os.system(cmd):
-                print 'error while backuping the base'
-                answer = self.confirm('continue anyway?',
-                                      shell=False, abort=False, retry=True)
-                if not answer:
-                    raise SystemExit(1)
-                if answer == 1: # 1: continue, 2: retry
-                    break
-            else:
-                from cubicweb.toolsutils import restrict_perms_to_user
-                print 'database backup:', backupfile
-                restrict_perms_to_user(backupfile, self.info)
-                break
+        repo = self.repo_connect()
+        timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
+        for source in repo.sources:
+            source.backup(self.confirm, backupfile, timestamp,
+                          askconfirm=askconfirm)
+        repo.hm.call_hooks('server_backup', repo=repo, timestamp=timestamp)
 
-    def restore_database(self, backupfile, drop=True):
+    def restore_database(self, backupfile, drop=True, systemonly=True,
+                         askconfirm=True):
         config = self.config
-        source = config.sources()['system']
-        helper = get_adv_func_helper(source['db-driver'])
-        app = config.appid
-        if not exists(backupfile):
-            raise Exception("backup file %s doesn't exist" % backupfile)
-        if self.confirm('restore %s database from %s ?' % (app, backupfile)):
-            for cmd in helper.restore_commands(source['db-name'], source.get('db-host'),
-                                               source.get('db-user'), backupfile,
-                                               source['db-encoding'],
-                                               keepownership=False, drop=drop):
-                while True:
-                    print cmd
-                    if os.system(cmd):
-                        print 'error while restoring the base'
-                        answer = self.confirm('continue anyway?',
-                                              shell=False, abort=False, retry=True)
-                        if not answer:
-                            raise SystemExit(1)
-                        if answer == 1: # 1: continue, 2: retry
-                            break
-                    else:
-                        break
-            print 'database restored'
+        repo = self.repo_connect()
+        if systemonly:
+            repo.system_source.restore(self.confirm, backupfile=backupfile,
+                                       drop=drop, askconfirm=askconfirm)
+        else:
+            # in that case, backup file is expected to be a time stamp
+            for source in repo.sources:
+                source.backup(self.confirm, timestamp=backupfile, drop=drop,
+                              askconfirm=askconfirm)
+            repo.hm.call_hooks('server_restore', repo=repo, timestamp=backupfile)
 
     @property
     def cnx(self):
@@ -478,7 +443,7 @@
     def cmd_add_cubes(self, cubes, update_database=True):
         """update_database is telling if the database schema should be updated
         or if only the relevant eproperty should be inserted (for the case where
-        a cube has been extracted from an existing application, so the
+        a cube has been extracted from an existing instance, so the
         cube schema is already in there)
         """
         newcubes = super(ServerMigrationHelper, self).cmd_add_cubes(cubes)
@@ -643,7 +608,7 @@
                 rtypeadded = rschema.type in applschema
                 for targetschema in rschema.objects(etype):
                     # ignore relations where the targeted type is not in the
-                    # current application schema
+                    # current instance schema
                     targettype = targetschema.type
                     if not targettype in applschema and targettype != etype:
                         continue
@@ -663,7 +628,7 @@
                 rtypeadded = rschema.type in applschema or rschema.type in added
                 for targetschema in rschema.subjects(etype):
                     # ignore relations where the targeted type is not in the
-                    # current application schema
+                    # current instance schema
                     targettype = targetschema.type
                     # don't check targettype != etype since in this case the
                     # relation has already been added as a subject relation
--- a/server/querier.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/querier.py	Fri Jul 31 14:25:30 2009 +0200
@@ -519,7 +519,7 @@
     def __init__(self, repo, schema):
         # system info helper
         self._repo = repo
-        # application schema
+        # instance schema
         self.set_schema(schema)
 
     def set_schema(self, schema):
--- a/server/repository.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/repository.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,7 @@
 repository mainly:
 
 * brings these classes all together to provide a single access
-  point to a cubicweb application.
+  point to a cubicweb instance.
 * handles session management
 * provides method for pyro registration, to call if pyro is enabled
 
@@ -179,12 +179,12 @@
         # open some connections pools
         self._available_pools = Queue.Queue()
         self._available_pools.put_nowait(ConnectionsPool(self.sources))
-        if config.read_application_schema:
-            # normal start: load the application schema from the database
+        if config.read_instance_schema:
+            # normal start: load the instance schema from the database
             self.fill_schema()
         elif config.bootstrap_schema:
             # usually during repository creation
-            self.warning("set fs application'schema as bootstrap schema")
+            self.warning("set fs instance'schema as bootstrap schema")
             config.bootstrap_cubes()
             self.set_bootstrap_schema(self.config.load_schema())
             # need to load the Any and CWUser entity types
@@ -197,7 +197,7 @@
                                 'cubicweb.entities.authobjs')
         else:
             # test start: use the file system schema (quicker)
-            self.warning("set fs application'schema")
+            self.warning("set fs instance'schema")
             config.bootstrap_cubes()
             self.set_schema(self.config.load_schema())
         if not config.creating:
@@ -217,11 +217,14 @@
         # close initialization pool and reopen fresh ones for proper
         # initialization now that we know cubes
         self._get_pool().close(True)
+        # list of available pools (we can't iterated on Queue instance)
+        self.pools = []
         for i in xrange(config['connections-pool-size']):
-            self._available_pools.put_nowait(ConnectionsPool(self.sources))
+            self.pools.append(ConnectionsPool(self.sources))
+            self._available_pools.put_nowait(self.pools[-1])
         self._shutting_down = False
-        if not config.creating:
-            # call application level initialisation hooks
+        if not (config.creating or config.repairing):
+            # call instance level initialisation hooks
             self.hm.call_hooks('server_startup', repo=self)
             # register a task to cleanup expired session
             self.looping_task(self.config['session-time']/3.,
@@ -247,9 +250,9 @@
             self.vreg.set_schema(schema)
         self.hm.set_schema(schema)
         self.hm.register_system_hooks(self.config)
-        # application specific hooks
-        if self.config.application_hooks:
-            self.info('loading application hooks')
+        # instance specific hooks
+        if self.config.instance_hooks:
+            self.info('loading instance hooks')
             self.hm.register_hooks(self.config.load_hooks(self.vreg))
 
     def fill_schema(self):
@@ -297,13 +300,13 @@
         config.usergroup_hooks = False
         config.schema_hooks = False
         config.notification_hooks = False
-        config.application_hooks = False
+        config.instance_hooks = False
         self.set_schema(schema, resetvreg=False)
         config.core_hooks = True
         config.usergroup_hooks = True
         config.schema_hooks = True
         config.notification_hooks = True
-        config.application_hooks = True
+        config.instance_hooks = True
 
     def start_looping_tasks(self):
         assert isinstance(self._looping_tasks, list), 'already started'
@@ -441,7 +444,7 @@
     # public (dbapi) interface ################################################
 
     def get_schema(self):
-        """return the application schema. This is a public method, not
+        """return the instance schema. This is a public method, not
         requiring a session id
         """
         try:
@@ -452,17 +455,18 @@
             self.schema.__hashmode__ = None
 
     def get_cubes(self):
-        """return the list of cubes used by this application. This is a
+        """return the list of cubes used by this instance. This is a
         public method, not requiring a session id.
         """
-        versions = self.get_versions(not self.config.creating)
+        versions = self.get_versions(not (self.config.creating
+                                          or self.config.repairing))
         cubes = list(versions)
         cubes.remove('cubicweb')
         return cubes
 
     @cached
     def get_versions(self, checkversions=False):
-        """return the a dictionary containing cubes used by this application
+        """return the a dictionary containing cubes used by this instance
         as key with their version as value, including cubicweb version. This is a
         public method, not requiring a session id.
         """
@@ -485,7 +489,7 @@
                     else:
                         fsversion = self.config.cubicweb_version()
                     if version < fsversion:
-                        msg = ('application has %s version %s but %s '
+                        msg = ('instance has %s version %s but %s '
                                'is installed. Run "cubicweb-ctl upgrade".')
                         raise ExecutionError(msg % (cube, version, fsversion))
         finally:
--- a/server/schemahooks.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/schemahooks.py	Fri Jul 31 14:25:30 2009 +0200
@@ -21,12 +21,12 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.pool import Operation, SingleLastOperation, PreCommitOperation
 from cubicweb.server.hookhelper import (entity_attr, entity_name,
-                                     check_internal_entity)
+                                        check_internal_entity)
 
 # core entity and relation types which can't be removed
 CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup',
                                   'CWConstraint', 'CWAttribute', 'CWRelation']
-CORE_RTYPES = ['eid', 'creation_date', 'modification_date',
+CORE_RTYPES = ['eid', 'creation_date', 'modification_date', 'cwuri',
                'login', 'upassword', 'name',
                'is', 'instanceof', 'owned_by', 'created_by', 'in_group',
                'relation_type', 'from_entity', 'to_entity',
@@ -105,7 +105,7 @@
 
 
 class DropTableOp(PreCommitOperation):
-    """actually remove a database from the application's schema"""
+    """actually remove a database from the instance's schema"""
     table = None # make pylint happy
     def precommit_event(self):
         dropped = self.session.transaction_data.setdefault('droppedtables',
@@ -137,7 +137,7 @@
 # deletion ####################################################################
 
 class DeleteCWETypeOp(SchemaOperation):
-    """actually remove the entity type from the application's schema"""
+    """actually remove the entity type from the instance's schema"""
     def commit_event(self):
         try:
             # del_entity_type also removes entity's relations
@@ -166,7 +166,7 @@
 
 
 class DeleteCWRTypeOp(SchemaOperation):
-    """actually remove the relation type from the application's schema"""
+    """actually remove the relation type from the instance's schema"""
     def commit_event(self):
         try:
             self.schema.del_relation_type(self.kobj)
@@ -189,8 +189,8 @@
     DeleteCWRTypeOp(session, name)
 
 
-class DelRelationDefOp(SchemaOperation):
-    """actually remove the relation definition from the application's schema"""
+class DeleteRelationDefOp(SchemaOperation):
+    """actually remove the relation definition from the instance's schema"""
     def commit_event(self):
         subjtype, rtype, objtype = self.kobj
         try:
@@ -238,13 +238,13 @@
     # if this is the last instance, drop associated relation type
     if lastrel and not rteid in pendings:
         execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x')
-    DelRelationDefOp(session, (subjschema, rschema, objschema))
+    DeleteRelationDefOp(session, (subjschema, rschema, objschema))
 
 
 # addition ####################################################################
 
 class AddCWETypeOp(EarlySchemaOperation):
-    """actually add the entity type to the application's schema"""
+    """actually add the entity type to the instance's schema"""
     eid = None # make pylint happy
     def commit_event(self):
         self.schema.add_entity_type(self.kobj)
@@ -264,7 +264,7 @@
     * set creation_date and modification_date by creating the necessary
       CWAttribute entities
     * add owned_by relation by creating the necessary CWRelation entity
-    * register an operation to add the entity type to the application's
+    * register an operation to add the entity type to the instance's
       schema on commit
     """
     if entity.get('final'):
@@ -283,7 +283,7 @@
                            prefix=SQL_PREFIX)
     relrqls = []
     for rtype in ('is', 'is_instance_of', 'creation_date', 'modification_date',
-                  'created_by', 'owned_by'):
+                  'cwuri', 'created_by', 'owned_by'):
         rschema = schema[rtype]
         sampletype = rschema.subjects()[0]
         desttype = rschema.objects()[0]
@@ -306,7 +306,7 @@
 
 
 class AddCWRTypeOp(EarlySchemaOperation):
-    """actually add the relation type to the application's schema"""
+    """actually add the relation type to the instance's schema"""
     eid = None # make pylint happy
     def commit_event(self):
         rschema = self.schema.add_relation_type(self.kobj)
@@ -315,7 +315,7 @@
 def before_add_ertype(session, entity):
     """before adding a CWRType entity:
     * check that we are not using an existing relation type,
-    * register an operation to add the relation type to the application's
+    * register an operation to add the relation type to the instance's
       schema on commit
 
     We don't know yeat this point if a table is necessary
@@ -326,7 +326,7 @@
 
 def after_add_ertype(session, entity):
     """after a CWRType entity has been added:
-    * register an operation to add the relation type to the application's
+    * register an operation to add the relation type to the instance's
       schema on commit
     We don't know yeat this point if a table is necessary
     """
@@ -340,7 +340,7 @@
 
 
 class AddErdefOp(EarlySchemaOperation):
-    """actually add the attribute relation definition to the application's
+    """actually add the attribute relation definition to the instance's
     schema
     """
     def commit_event(self):
@@ -363,7 +363,7 @@
     * add the necessary column
     * set default on this column if any and possible
     * register an operation to add the relation definition to the
-      application's schema on commit
+      instance's schema on commit
 
     constraints are handled by specific hooks
     """
@@ -436,7 +436,7 @@
       else if it's the first instance of this relation type, add the
       necessary table and set default permissions
     * register an operation to add the relation definition to the
-      application's schema on commit
+      instance's schema on commit
 
     constraints are handled by specific hooks
     """
@@ -883,7 +883,7 @@
                 self.perm, erschema.type, self.group)
 
 
-class DelRQLExpressionPermissionOp(AddRQLExpressionPermissionOp):
+class DeleteRQLExpressionPermissionOp(AddRQLExpressionPermissionOp):
     """synchronize schema when a *_permission relation has been deleted from an rql expression"""
 
     def commit_event(self):
@@ -919,7 +919,7 @@
     else: # RQLExpression
         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
                                {'x': object}, 'x')[0][0]
-        DelRQLExpressionPermissionOp(session, perm, subject, expr)
+        DeleteRQLExpressionPermissionOp(session, perm, subject, expr)
 
 
 def rebuild_infered_relations(session, subject, rtype, object):
--- a/server/schemaserial.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/schemaserial.py	Fri Jul 31 14:25:30 2009 +0200
@@ -114,10 +114,10 @@
     index = {}
     permsdict = deserialize_ertype_permissions(session)
     schema.reading_from_database = True
-    for eid, etype, desc, meta in session.execute('Any X, N, D, M WHERE '
-                                                  'X is CWEType, X name N, '
-                                                  'X description D, X meta M',
-                                                  build_descr=False):
+    for eid, etype, desc in session.execute('Any X, N, D WHERE '
+                                            'X is CWEType, X name N, '
+                                            'X description D',
+                                            build_descr=False):
         # base types are already in the schema, skip them
         if etype in schemamod.BASE_TYPES:
             # just set the eid
@@ -152,7 +152,7 @@
             repo.clear_caches(tocleanup)
             session.commit(False)
             etype = netype
-        etype = ybo.EntityType(name=etype, description=desc, meta=meta, eid=eid)
+        etype = ybo.EntityType(name=etype, description=desc, eid=eid)
         eschema = schema.add_entity_type(etype)
         index[eid] = eschema
         set_perms(eschema, permsdict.get(eid, {}))
@@ -167,9 +167,9 @@
             seschema = schema.eschema(stype)
             eschema._specialized_type = stype
             seschema._specialized_by.append(etype)
-    for eid, rtype, desc, meta, sym, il in session.execute(
-        'Any X,N,D,M,S,I WHERE X is CWRType, X name N, X description D, '
-        'X meta M, X symetric S, X inlined I', build_descr=False):
+    for eid, rtype, desc, sym, il in session.execute(
+        'Any X,N,D,S,I WHERE X is CWRType, X name N, X description D, '
+        'X symetric S, X inlined I', build_descr=False):
         try:
             # bw compat: fulltext_container added in 2.47
             ft_container = session.execute('Any FTC WHERE X eid %(x)s, X fulltext_container FTC',
@@ -177,7 +177,7 @@
         except:
             ft_container = None
             session.rollback(False)
-        rtype = ybo.RelationType(name=rtype, description=desc, meta=bool(meta),
+        rtype = ybo.RelationType(name=rtype, description=desc,
                                  symetric=bool(sym), inlined=bool(il),
                                  fulltext_container=ft_container, eid=eid)
         rschema = schema.add_relation_type(rtype)
@@ -327,7 +327,6 @@
         raise Exception("can't decode %s [was %s]" % (erschema.description, e))
     return {
         'name': type_,
-        'meta': erschema.meta,
         'final': erschema.is_final(),
         'description': desc,
         }
--- a/server/serverconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/serverconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -171,7 +171,7 @@
           'help': 'Pyro server port. If not set, it will be choosen randomly',
           'group': 'pyro-server', 'inputlevel': 2,
           }),
-        ('pyro-id', # XXX reuse pyro-application-id
+        ('pyro-id', # XXX reuse pyro-instance-id
          {'type' : 'string',
           'default': None,
           'help': 'identifier of the repository in the pyro name server',
@@ -180,7 +180,7 @@
         ) + CubicWebConfiguration.options)
 
     # read the schema from the database
-    read_application_schema = True
+    read_instance_schema = True
     bootstrap_schema = True
 
     # check user's state at login time
@@ -193,7 +193,7 @@
     schema_hooks = True
     notification_hooks = True
     security_hooks = True
-    application_hooks = True
+    instance_hooks = True
 
     # should some hooks be deactivated during [pre|post]create script execution
     free_wheel = False
@@ -208,14 +208,9 @@
 
     @classmethod
     def schemas_lib_dir(cls):
-        """application schema directory"""
+        """instance schema directory"""
         return env_path('CW_SCHEMA_LIB', cls.SCHEMAS_LIB_DIR, 'schemas')
 
-    @classmethod
-    def backup_dir(cls):
-        """backup directory where a stored db backups before migration"""
-        return env_path('CW_BACKUP', cls.BACKUP_DIR, 'run time')
-
     def bootstrap_cubes(self):
         from logilab.common.textutils import get_csv
         for line in file(join(self.apphome, 'bootstrap_cubes')):
@@ -269,23 +264,6 @@
 
     def load_hooks(self, vreg):
         hooks = {}
-        for path in reversed([self.apphome] + self.cubes_path()):
-            hooksfile = join(path, 'application_hooks.py')
-            if exists(hooksfile):
-                self.warning('application_hooks.py is deprecated, use dynamic '
-                             'objects to register hooks (%s)', hooksfile)
-                context = {}
-                # Use execfile rather than `load_module_from_name` because
-                # the latter gets fooled by the `sys.modules` cache when
-                # loading different configurations one after the other
-                # (another fix would have been to do :
-                #    sys.modules.pop('applications_hooks')
-                #  or to modify load_module_from_name so that it provides
-                #  a use_cache optional parameter
-                execfile(hooksfile, context, context)
-                for event, hooksdef in context['HOOKS'].items():
-                    for ertype, hookcbs in hooksdef.items():
-                        hooks.setdefault(event, {}).setdefault(ertype, []).extend(hookcbs)
         try:
             apphookdefs = vreg.registry_objects('hooks')
         except RegistryNotFound:
@@ -342,9 +320,11 @@
         clear_cache(self, 'sources')
 
     def migration_handler(self, schema=None, interactive=True,
-                          cnx=None, repo=None, connect=True):
+                          cnx=None, repo=None, connect=True, verbosity=None):
         """return a migration handler instance"""
         from cubicweb.server.migractions import ServerMigrationHelper
+        if verbosity is None:
+            verbosity = getattr(self, 'verbosity', 0)
         return ServerMigrationHelper(self, schema, interactive=interactive,
                                      cnx=cnx, repo=repo, connect=connect,
-                                     verbosity=getattr(self, 'verbosity', 0))
+                                     verbosity=verbosity)
--- a/server/serverctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/serverctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,7 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-__docformat__ = "restructuredtext en"
+__docformat__ = 'restructuredtext en'
 
 import sys
 import os
@@ -58,10 +58,10 @@
 
 def system_source_cnx(source, dbms_system_base=False,
                       special_privs='CREATE/DROP DATABASE', verbose=True):
-    """shortcut to get a connextion to the application system database
+    """shortcut to get a connextion to the instance system database
     defined in the given config. If <dbms_system_base> is True,
     connect to the dbms system database instead (for task such as
-    create/drop the application database)
+    create/drop the instance database)
     """
     if dbms_system_base:
         from logilab.common.adbh import get_adv_func_helper
@@ -73,7 +73,9 @@
     """return a connection on the RDMS system table (to create/drop a user
     or a database
     """
+    import logilab.common as lgp
     from logilab.common.adbh import get_adv_func_helper
+    lgp.USE_MX_DATETIME = False
     special_privs = ''
     driver = source['db-driver']
     helper = get_adv_func_helper(driver)
@@ -117,7 +119,7 @@
     cfgname = 'repository'
 
     def bootstrap(self, cubes, inputlevel=0):
-        """create an application by copying files from the given cube and by
+        """create an instance by copying files from the given cube and by
         asking information necessary to build required configuration files
         """
         config = self.config
@@ -167,7 +169,7 @@
         config.write_bootstrap_cubes_file(cubes)
 
     def postcreate(self):
-        if confirm('Do you want to run db-create to create the "system database" ?'):
+        if confirm('Do you want to run db-create to create the system database ?'):
             verbosity = (self.config.mode == 'installed') and 'y' or 'n'
             cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity)
         else:
@@ -180,7 +182,7 @@
     cfgname = 'repository'
 
     def cleanup(self):
-        """remove application's configuration and database"""
+        """remove instance's configuration and database"""
         from logilab.common.adbh import get_adv_func_helper
         source = self.config.sources()['system']
         dbname = source['db-name']
@@ -229,26 +231,26 @@
 
 
 # repository specific commands ################################################
-class CreateApplicationDBCommand(Command):
-    """Create the system database of an application (run after 'create').
+class CreateInstanceDBCommand(Command):
+    """Create the system database of an instance (run after 'create').
 
     You will be prompted for a login / password to use to connect to
     the system database.  The given user should have almost all rights
     on the database (ie a super user on the dbms allowed to create
     database, users, languages...).
 
-    <application>
-      the identifier of the application to initialize.
+    <instance>
+      the identifier of the instance to initialize.
     """
     name = 'db-create'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = (
-        ("create-db",
-         {'short': 'c', 'type': "yn", 'metavar': '<y or n>',
+        ('create-db',
+         {'short': 'c', 'type': 'yn', 'metavar': '<y or n>',
           'default': True,
           'help': 'create the database (yes by default)'}),
-        ("verbose",
+        ('verbose',
          {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>',
           'default': 'n',
           'help': 'verbose mode: will ask all possible configuration questions',
@@ -260,14 +262,14 @@
         from logilab.common.adbh import get_adv_func_helper
         from indexer import get_indexer
         verbose = self.get('verbose')
-        appid = pop_arg(args, msg="No application specified !")
+        appid = pop_arg(args, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         create_db = self.config.create_db
         source = config.sources()['system']
         driver = source['db-driver']
         helper = get_adv_func_helper(driver)
         if create_db:
-            print '\n'+underline_title('Creating the "system database"')
+            print '\n'+underline_title('Creating the system database')
             # connect on the dbms system base to create our base
             dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose)
             cursor = dbcnx.cursor()
@@ -306,30 +308,30 @@
                 helper.create_language(cursor, extlang)
         cursor.close()
         cnx.commit()
-        print '-> database for application %s created and necessary extensions installed.' % appid
+        print '-> database for instance %s created and necessary extensions installed.' % appid
         print
-        if confirm('Do you want to run db-init to initialize the "system database" ?'):
+        if confirm('Do you want to run db-init to initialize the system database ?'):
             cmd_run('db-init', config.appid)
         else:
             print ('-> nevermind, you can do it later with '
                    '"cubicweb-ctl db-init %s".' % self.config.appid)
 
 
-class InitApplicationCommand(Command):
-    """Initialize the system database of an application (run after 'db-create').
+class InitInstanceCommand(Command):
+    """Initialize the system database of an instance (run after 'db-create').
 
     You will be prompted for a login / password to use to connect to
     the system database.  The given user should have the create tables,
     and grant permissions.
 
-    <application>
-      the identifier of the application to initialize.
+    <instance>
+      the identifier of the instance to initialize.
     """
     name = 'db-init'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = (
-        ("drop",
+        ('drop',
          {'short': 'd', 'action': 'store_true',
           'default': False,
           'help': 'insert drop statements to remove previously existant \
@@ -337,27 +339,27 @@
         )
 
     def run(self, args):
-        print '\n'+underline_title('Initializing the "system database"')
+        print '\n'+underline_title('Initializing the system database')
         from cubicweb.server import init_repository
-        appid = pop_arg(args, msg="No application specified !")
+        appid = pop_arg(args, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         init_repository(config, drop=self.config.drop)
 
 
-class GrantUserOnApplicationCommand(Command):
+class GrantUserOnInstanceCommand(Command):
     """Grant a database user on a repository system database.
 
-    <application>
-      the identifier of the application
+    <instance>
+      the identifier of the instance
     <user>
       the database's user requiring grant access
     """
     name = 'db-grant-user'
-    arguments = '<application> <user>'
+    arguments = '<instance> <user>'
 
     options = (
-        ("set-owner",
-         {'short': 'o', 'type' : "yn", 'metavar' : '<yes or no>',
+        ('set-owner',
+         {'short': 'o', 'type' : 'yn', 'metavar' : '<yes or no>',
           'default' : False,
           'help': 'Set the user as tables owner if yes (no by default).'}
          ),
@@ -365,8 +367,8 @@
     def run(self, args):
         """run the command with its specific arguments"""
         from cubicweb.server.sqlutils import sqlexec, sqlgrants
-        appid = pop_arg(args, 1, msg="No application specified !")
-        user = pop_arg(args, msg="No user specified !")
+        appid = pop_arg(args, 1, msg='No instance specified !')
+        user = pop_arg(args, msg='No user specified !')
         config = ServerConfiguration.config_for(appid)
         source = config.sources()['system']
         set_owner = self.config.set_owner
@@ -383,22 +385,22 @@
             print '-> an error occured:', ex
         else:
             cnx.commit()
-            print '-> rights granted to %s on application %s.' % (appid, user)
+            print '-> rights granted to %s on instance %s.' % (appid, user)
 
 class ResetAdminPasswordCommand(Command):
     """Reset the administrator password.
 
-    <application>
-      the identifier of the application
+    <instance>
+      the identifier of the instance
     """
     name = 'reset-admin-pwd'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     def run(self, args):
         """run the command with its specific arguments"""
         from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
         from cubicweb.server.utils import crypt_password, manager_userpasswd
-        appid = pop_arg(args, 1, msg="No application specified !")
+        appid = pop_arg(args, 1, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         sourcescfg = config.read_sources_file()
         try:
@@ -431,25 +433,25 @@
 
 
 class StartRepositoryCommand(Command):
-    """Start an CubicWeb RQL server for a given application.
+    """Start an CubicWeb RQL server for a given instance.
 
     The server will be accessible through pyro
 
-    <application>
-      the identifier of the application to initialize.
+    <instance>
+      the identifier of the instance to initialize.
     """
     name = 'start-repository'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = (
-        ("debug",
+        ('debug',
          {'short': 'D', 'action' : 'store_true',
           'help': 'start server in debug mode.'}),
         )
 
     def run(self, args):
         from cubicweb.server.server import RepositoryServer
-        appid = pop_arg(args, msg="No application specified !")
+        appid = pop_arg(args, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         debug = self.config.debug
         # create the server
@@ -471,6 +473,7 @@
 
 
 def _remote_dump(host, appid, output, sudo=False):
+    # XXX generate unique/portable file name
     dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s.dump %s' % (appid, appid)
     if sudo:
         dmpcmd = 'sudo %s' % (dmpcmd)
@@ -495,14 +498,17 @@
 def _local_dump(appid, output):
     config = ServerConfiguration.config_for(appid)
     # schema=1 to avoid unnecessary schema loading
-    mih = config.migration_handler(connect=False, schema=1)
+    mih = config.migration_handler(connect=False, schema=1, verbosity=1)
     mih.backup_database(output, askconfirm=False)
+    mih.shutdown()
 
-def _local_restore(appid, backupfile, drop):
+def _local_restore(appid, backupfile, drop, systemonly=True):
     config = ServerConfiguration.config_for(appid)
+    config.verbosity = 1 # else we won't be asked for confirmation on problems
+    config.repairing = 1 # don't check versions
     # schema=1 to avoid unnecessary schema loading
-    mih = config.migration_handler(connect=False, schema=1)
-    mih.restore_database(backupfile, drop)
+    mih = config.migration_handler(connect=False, schema=1, verbosity=1)
+    mih.restore_database(backupfile, drop, systemonly, askconfirm=False)
     repo = mih.repo_connect()
     # version of the database
     dbversions = repo.get_versions()
@@ -512,7 +518,7 @@
         return
     # version of installed software
     eversion = dbversions['cubicweb']
-    status = application_status(config, eversion, dbversions)
+    status = instance_status(config, eversion, dbversions)
     # * database version > installed software
     if status == 'needsoftupgrade':
         print "database is using some earlier version than installed software!"
@@ -530,7 +536,7 @@
     #   ok!
 
 
-def application_status(config, cubicwebapplversion, vcconf):
+def instance_status(config, cubicwebapplversion, vcconf):
     cubicwebversion = config.cubicweb_version()
     if cubicwebapplversion > cubicwebversion:
         return 'needsoftupgrade'
@@ -540,12 +546,12 @@
         try:
             softversion = config.cube_version(cube)
         except ConfigurationError:
-            print "-> Error: no cube version information for %s, please check that the cube is installed." % cube
+            print '-> Error: no cube version information for %s, please check that the cube is installed.' % cube
             continue
         try:
             applversion = vcconf[cube]
         except KeyError:
-            print "-> Error: no cube version information for %s in version configuration." % cube
+            print '-> Error: no cube version information for %s in version configuration.' % cube
             continue
         if softversion == applversion:
             continue
@@ -557,18 +563,18 @@
 
 
 class DBDumpCommand(Command):
-    """Backup the system database of an application.
+    """Backup the system database of an instance.
 
-    <application>
-      the identifier of the application to backup
+    <instance>
+      the identifier of the instance to backup
       format [[user@]host:]appname
     """
     name = 'db-dump'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = (
-        ("output",
-         {'short': 'o', 'type' : "string", 'metavar' : '<file>',
+        ('output',
+         {'short': 'o', 'type' : 'string', 'metavar' : '<file>',
           'default' : None,
           'help': 'Specify the backup file where the backup will be stored.'}
          ),
@@ -580,7 +586,7 @@
         )
 
     def run(self, args):
-        appid = pop_arg(args, 1, msg="No application specified !")
+        appid = pop_arg(args, 1, msg='No instance specified !')
         if ':' in appid:
             host, appid = appid.split(':')
             _remote_dump(host, appid, self.config.output, self.config.sudo)
@@ -589,50 +595,58 @@
 
 
 class DBRestoreCommand(Command):
-    """Restore the system database of an application.
+    """Restore the system database of an instance.
 
-    <application>
-      the identifier of the application to restore
+    <instance>
+      the identifier of the instance to restore
     """
     name = 'db-restore'
-    arguments = '<application> <backupfile>'
+    arguments = '<instance> <backupfile>'
 
     options = (
-        ("no-drop",
-         {'short': 'n', 'action' : 'store_true',
-          'default' : False,
+        ('no-drop',
+         {'short': 'n', 'action' : 'store_true', 'default' : False,
           'help': 'for some reason the database doesn\'t exist and so '
           'should not be dropped.'}
          ),
+        ('restore-all',
+         {'short': 'r', 'action' : 'store_true', 'default' : False,
+          'help': 'restore everything, eg not only the system source database '
+          'but also data for all sources supporting backup/restore and custom '
+          'instance data. In that case, <backupfile> is expected to be the '
+          'timestamp of the backup to restore, not a file'}
+         ),
         )
 
     def run(self, args):
-        appid = pop_arg(args, 1, msg="No application specified !")
-        backupfile = pop_arg(args, msg="No backup file specified !")
-        _local_restore(appid, backupfile, not self.config.no_drop)
+        appid = pop_arg(args, 1, msg='No instance specified !')
+        backupfile = pop_arg(args, msg='No backup file or timestamp specified !')
+        _local_restore(appid, backupfile,
+                       drop=not self.config.no_drop,
+                       systemonly=not self.config.restore_all)
 
 
 class DBCopyCommand(Command):
-    """Copy the system database of an application (backup and restore).
+    """Copy the system database of an instance (backup and restore).
 
-    <src-application>
-      the identifier of the application to backup
+    <src-instance>
+      the identifier of the instance to backup
       format [[user@]host:]appname
 
-    <dest-application>
-      the identifier of the application to restore
+    <dest-instance>
+      the identifier of the instance to restore
     """
     name = 'db-copy'
-    arguments = '<src-application> <dest-application>'
+    arguments = '<src-instance> <dest-instance>'
 
     options = (
-        ("no-drop",
+        ('no-drop',
          {'short': 'n', 'action' : 'store_true',
           'default' : False,
           'help': 'For some reason the database doesn\'t exist and so '
           'should not be dropped.'}
          ),
-        ("keep-dump",
+        ('keep-dump',
          {'short': 'k', 'action' : 'store_true',
           'default' : False,
           'help': 'Specify that the dump file should not be automatically removed.'}
@@ -646,9 +660,9 @@
 
     def run(self, args):
         import tempfile
-        srcappid = pop_arg(args, 1, msg="No source application specified !")
-        destappid = pop_arg(args, msg="No destination application specified !")
-        output = tempfile.mktemp()
+        srcappid = pop_arg(args, 1, msg='No source instance specified !')
+        destappid = pop_arg(args, msg='No destination instance specified !')
+        _, output = tempfile.mkstemp()
         if ':' in srcappid:
             host, srcappid = srcappid.split(':')
             _remote_dump(host, srcappid, output, self.config.sudo)
@@ -662,60 +676,66 @@
 
 
 class CheckRepositoryCommand(Command):
-    """Check integrity of the system database of an application.
+    """Check integrity of the system database of an instance.
 
-    <application>
-      the identifier of the application to check
+    <instance>
+      the identifier of the instance to check
     """
     name = 'db-check'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = (
-        ("checks",
-         {'short': 'c', 'type' : "csv", 'metavar' : '<check list>',
+        ('checks',
+         {'short': 'c', 'type' : 'csv', 'metavar' : '<check list>',
           'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'),
           'help': 'Comma separated list of check to run. By default run all \
 checks, i.e. entities, relations, text_index and metadata.'}
          ),
 
-        ("autofix",
-         {'short': 'a', 'type' : "yn", 'metavar' : '<yes or no>',
+        ('autofix',
+         {'short': 'a', 'type' : 'yn', 'metavar' : '<yes or no>',
           'default' : False,
           'help': 'Automatically correct integrity problems if this option \
 is set to "y" or "yes", else only display them'}
          ),
-        ("reindex",
-         {'short': 'r', 'type' : "yn", 'metavar' : '<yes or no>',
+        ('reindex',
+         {'short': 'r', 'type' : 'yn', 'metavar' : '<yes or no>',
           'default' : False,
           'help': 're-indexes the database for full text search if this \
 option is set to "y" or "yes" (may be long for large database).'}
          ),
+        ('force',
+         {'short': 'f', 'action' : 'store_true',
+          'default' : False,
+          'help': 'don\'t check instance is up to date.'}
+         ),
 
         )
 
     def run(self, args):
         from cubicweb.server.checkintegrity import check
-        appid = pop_arg(args, 1, msg="No application specified !")
+        appid = pop_arg(args, 1, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
+        config.repairing = self.config.force
         repo, cnx = repo_cnx(config)
         check(repo, cnx,
               self.config.checks, self.config.reindex, self.config.autofix)
 
 
 class RebuildFTICommand(Command):
-    """Rebuild the full-text index of the system database of an application.
+    """Rebuild the full-text index of the system database of an instance.
 
-    <application>
-      the identifier of the application to rebuild
+    <instance>
+      the identifier of the instance to rebuild
     """
     name = 'db-rebuild-fti'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     options = ()
 
     def run(self, args):
         from cubicweb.server.checkintegrity import reindex_entities
-        appid = pop_arg(args, 1, msg="No application specified !")
+        appid = pop_arg(args, 1, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         repo, cnx = repo_cnx(config)
         session = repo._get_session(cnx.sessionid, setpool=True)
@@ -723,28 +743,28 @@
         cnx.commit()
 
 
-class SynchronizeApplicationSchemaCommand(Command):
+class SynchronizeInstanceSchemaCommand(Command):
     """Synchronize persistent schema with cube schema.
 
     Will synchronize common stuff between the cube schema and the
     actual persistent schema, but will not add/remove any entity or relation.
 
-    <application>
-      the identifier of the application to synchronize.
+    <instance>
+      the identifier of the instance to synchronize.
     """
     name = 'schema-sync'
-    arguments = '<application>'
+    arguments = '<instance>'
 
     def run(self, args):
-        appid = pop_arg(args, msg="No application specified !")
+        appid = pop_arg(args, msg='No instance specified !')
         config = ServerConfiguration.config_for(appid)
         mih = config.migration_handler()
         mih.cmd_synchronize_schema()
 
 
-register_commands( (CreateApplicationDBCommand,
-                    InitApplicationCommand,
-                    GrantUserOnApplicationCommand,
+register_commands( (CreateInstanceDBCommand,
+                    InitInstanceCommand,
+                    GrantUserOnInstanceCommand,
                     ResetAdminPasswordCommand,
                     StartRepositoryCommand,
                     DBDumpCommand,
@@ -752,5 +772,5 @@
                     DBCopyCommand,
                     CheckRepositoryCommand,
                     RebuildFTICommand,
-                    SynchronizeApplicationSchemaCommand,
+                    SynchronizeInstanceSchemaCommand,
                     ) )
--- a/server/session.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/session.py	Fri Jul 31 14:25:30 2009 +0200
@@ -215,7 +215,14 @@
         raise KeyError(eid)
 
     def base_url(self):
-        return self.repo.config['base-url'] or u''
+        url = self.repo.config['base-url']
+        if not url:
+            try:
+                url = self.repo.config.default_base_url()
+            except AttributeError: # default_base_url() might not be available
+                self.warning('missing base-url definition in server config')
+                url = u''
+        return url
 
     def from_controller(self):
         """return the id (string) of the controller issuing the request (no
--- a/server/sources/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/sources/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -7,6 +7,7 @@
 """
 __docformat__ = "restructuredtext en"
 
+from os.path import join, splitext
 from datetime import datetime, timedelta
 from logging import getLogger
 
@@ -14,6 +15,7 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 
 
+
 class TimedCache(dict):
     def __init__(self, ttlm, ttls=0):
         # time to live in minutes
@@ -53,7 +55,7 @@
     uri = None
     # a reference to the system information helper
     repo = None
-    # a reference to the application'schema (may differs from the source'schema)
+    # a reference to the instance'schema (may differs from the source'schema)
     schema = None
 
     def __init__(self, repo, appschema, source_config, *args, **kwargs):
@@ -71,6 +73,42 @@
         """method called by the repository once ready to handle request"""
         pass
 
+    def backup_file(self, backupfile=None, timestamp=None):
+        """return a unique file name for a source's dump
+
+        either backupfile or timestamp (used to generated a backup file name if
+        needed) should be specified.
+        """
+        if backupfile is None:
+            config = self.repo.config
+            return join(config.appdatahome, 'backup',
+                        '%s-%s-%s.dump' % (config.appid, timestamp, self.uri))
+        # backup file is the system database backup file, add uri to it if not
+        # already there
+        base, ext = splitext(backupfile)
+        if not base.endswith('-%s' % self.uri):
+            return '%s-%s%s' % (base, self.uri, ext)
+        return backupfile
+
+    def backup(self, confirm, backupfile=None, timestamp=None,
+               askconfirm=False):
+        """method called to create a backup of source's data"""
+        pass
+
+    def restore(self, confirm, backupfile=None, timestamp=None, drop=True,
+               askconfirm=False):
+        """method called to restore a backup of source's data"""
+        pass
+
+    def close_pool_connections(self):
+        for pool in self.repo.pools:
+            pool._cursors.pop(self.uri, None)
+            pool.source_cnxs[self.uri][1].close()
+
+    def open_pool_connections(self):
+        for pool in self.repo.pools:
+            pool.source_cnxs[self.uri] = (self, self.get_connection())
+
     def reset_caches(self):
         """method called during test to reset potential source caches"""
         pass
@@ -95,7 +133,7 @@
         return cmp(self.uri, other.uri)
 
     def set_schema(self, schema):
-        """set the application'schema"""
+        """set the instance'schema"""
         self.schema = schema
 
     def support_entity(self, etype, write=False):
--- a/server/sources/extlite.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/sources/extlite.py	Fri Jul 31 14:25:30 2009 +0200
@@ -11,7 +11,8 @@
 from os.path import join, exists
 
 from cubicweb import server
-from cubicweb.server.sqlutils import SQL_PREFIX, sqlexec, SQLAdapterMixIn
+from cubicweb.server.sqlutils import (SQL_PREFIX, SQLAdapterMixIn, sqlexec,
+                                      sql_source_backup, sql_source_restore)
 from cubicweb.server.sources import AbstractSource, native
 from cubicweb.server.sources.rql2sql import SQLGenerator
 
@@ -85,6 +86,19 @@
         AbstractSource.__init__(self, repo, appschema, source_config,
                                 *args, **kwargs)
 
+    def backup(self, confirm, backupfile=None, timestamp=None, askconfirm=False):
+        """method called to create a backup of source's data"""
+        backupfile = self.backup_file(backupfile, timestamp)
+        sql_source_backup(self, self.sqladapter, confirm, backupfile,
+                          askconfirm)
+
+    def restore(self, confirm, backupfile=None, timestamp=None, drop=True,
+               askconfirm=False):
+        """method called to restore a backup of source's data"""
+        backupfile = self.backup_file(backupfile, timestamp)
+        sql_source_restore(self, self.sqladapter, confirm, backupfile, drop,
+                           askconfirm)
+
     @property
     def _sqlcnx(self):
         # XXX: sqlite connections can only be used in the same thread, so
--- a/server/sources/native.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/sources/native.py	Fri Jul 31 14:25:30 2009 +0200
@@ -25,7 +25,8 @@
 
 from cubicweb import UnknownEid, AuthenticationError, Binary, server
 from cubicweb.server.utils import crypt_password
-from cubicweb.server.sqlutils import SQL_PREFIX, SQLAdapterMixIn
+from cubicweb.server.sqlutils import (SQL_PREFIX, SQLAdapterMixIn,
+                                      sql_source_backup, sql_source_restore)
 from cubicweb.server.rqlannotation import set_qdata
 from cubicweb.server.sources import AbstractSource
 from cubicweb.server.sources.rql2sql import SQLGenerator
@@ -205,6 +206,18 @@
         pool.pool_reset()
         self.repo._free_pool(pool)
 
+    def backup(self, confirm, backupfile=None, timestamp=None,
+               askconfirm=False):
+        """method called to create a backup of source's data"""
+        backupfile = self.backup_file(backupfile, timestamp)
+        sql_source_backup(self, self, confirm, backupfile, askconfirm)
+
+    def restore(self, confirm, backupfile=None, timestamp=None, drop=True,
+               askconfirm=False):
+        """method called to restore a backup of source's data"""
+        backupfile = self.backup_file(backupfile, timestamp)
+        sql_source_restore(self, self, confirm, backupfile, drop, askconfirm)
+
     def init(self):
         self.init_creating()
         pool = self.repo._get_pool()
@@ -219,7 +232,7 @@
 
     def map_attribute(self, etype, attr, cb):
         self._rql_sqlgen.attr_map['%s.%s' % (etype, attr)] = cb
-        
+
     # ISource interface #######################################################
 
     def compile_rql(self, rql):
@@ -231,7 +244,7 @@
         return rqlst
 
     def set_schema(self, schema):
-        """set the application'schema"""
+        """set the instance'schema"""
         self._cache = Cache(self.repo.config['rql-cache-size'])
         self.cache_hit, self.cache_miss, self.no_cache = 0, 0, 0
         self.schema = schema
--- a/server/sqlutils.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/sqlutils.py	Fri Jul 31 14:25:30 2009 +0200
@@ -7,6 +7,8 @@
 """
 __docformat__ = "restructuredtext en"
 
+import os
+from os.path import exists
 from warnings import warn
 from datetime import datetime, date, timedelta
 
@@ -21,6 +23,7 @@
 from cubicweb import Binary, ConfigurationError
 from cubicweb.utils import todate, todatetime
 from cubicweb.common.uilib import remove_html_tags
+from cubicweb.toolsutils import restrict_perms_to_user
 from cubicweb.server import SQL_CONNECT_HOOKS
 from cubicweb.server.utils import crypt_password
 
@@ -116,6 +119,39 @@
                      skip_relations=skip_relations))
     return '\n'.join(output)
 
+
+def sql_source_backup(source, sqladapter, confirm, backupfile,
+                      askconfirm=False):
+    if exists(backupfile):
+        if not confirm('Backup file %s exists, overwrite it?' % backupfile):
+            return
+    elif askconfirm and not confirm('Backup %s database?'
+                                    % source.repo.config.appid):
+        print '-> no backup done.'
+        return
+    # should close opened connection before backuping
+    source.close_pool_connections()
+    try:
+        sqladapter.backup_to_file(backupfile, confirm)
+    finally:
+        source.open_pool_connections()
+
+def sql_source_restore(source, sqladapter, confirm, backupfile, drop=True,
+                       askconfirm=False):
+    if not exists(backupfile):
+        raise Exception("backup file %s doesn't exist" % backupfile)
+    app = source.repo.config.appid
+    if askconfirm and not confirm('Restore %s %s database from %s ?'
+                                  % (app, source.uri, backupfile)):
+        return
+    # should close opened connection before restoring
+    source.close_pool_connections()
+    try:
+        sqladapter.restore_from_file(backupfile, confirm, drop=drop)
+    finally:
+        source.open_pool_connections()
+
+
 try:
     from mx.DateTime import DateTimeType, DateTimeDeltaType
 except ImportError:
@@ -159,6 +195,46 @@
         #self.dbapi_module.type_code_test(cnx.cursor())
         return cnx
 
+    def backup_to_file(self, backupfile, confirm):
+        cmd = self.dbhelper.backup_command(self.dbname, self.dbhost,
+                                           self.dbuser, backupfile,
+                                           keepownership=False)
+        while True:
+            print cmd
+            if os.system(cmd):
+                print '-> error while backuping the base'
+                answer = confirm('Continue anyway?',
+                                 shell=False, abort=False, retry=True)
+                if not answer:
+                    raise SystemExit(1)
+                if answer == 1: # 1: continue, 2: retry
+                    break
+            else:
+                print '-> backup file',  backupfile
+                restrict_perms_to_user(backupfile, self.info)
+                break
+
+    def restore_from_file(self, backupfile, confirm, drop=True):
+        for cmd in self.dbhelper.restore_commands(self.dbname, self.dbhost,
+                                                  self.dbuser, backupfile,
+                                                  self.encoding,
+                                                  keepownership=False,
+                                                  drop=drop):
+            while True:
+                print cmd
+                if os.system(cmd):
+                    print 'error while restoring the base'
+                    print 'OOOOOPS', confirm
+                    answer = confirm('continue anyway?',
+                                     shell=False, abort=False, retry=True)
+                    if not answer:
+                        raise SystemExit(1)
+                    if answer == 1: # 1: continue, 2: retry
+                        break
+                else:
+                    break
+        print 'database restored'
+
     def merge_args(self, args, query_args):
         if args is not None:
             args = dict(args)
--- a/server/test/data/migrschema/Folder2.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/data/migrschema/Folder2.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,6 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from cubicweb.schema import format_constraint
 
 class Folder2(MetaUserEntityType):
     """folders are used to classify entities. They may be defined as a tree.
@@ -14,9 +13,7 @@
     """
     name = String(required=True, indexed=True, internationalizable=True,
                   constraints=[UniqueConstraint(), SizeConstraint(64)])
-    description_format = String(meta=True, internationalizable=True,
-                                default='text/rest', constraints=[format_constraint])
-    description = String(fulltextindexed=True)
+    description = RichString(fulltextindexed=True)
 
     filed_under2 = BothWayRelation(
         SubjectRelation('Folder2', description=_("parent folder")),
--- a/server/test/data/schema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/data/schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,7 +5,12 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from cubicweb.schema import format_constraint
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
+                            SubjectRelation, ObjectRelation,
+                            RichString, String, Int, Boolean, Datetime)
+from yams.constraints import SizeConstraint
+from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
+                             ERQLExpression, RRQLExpression)
 
 class Affaire(WorkflowableEntityType):
     permissions = {
@@ -20,10 +25,8 @@
                  constraints=[SizeConstraint(16)])
     sujet = String(fulltextindexed=True,
                    constraints=[SizeConstraint(256)])
-    descr_format = String(meta=True, internationalizable=True,
-                                default='text/rest', constraints=[format_constraint])
-    descr = String(fulltextindexed=True,
-                   description=_('more detailed description'))
+    descr = RichString(fulltextindexed=True,
+                       description=_('more detailed description'))
 
     duration = Int()
     invoiced = Int()
@@ -63,8 +66,8 @@
     __specializes_schema__ = True
     travaille_subdivision = ObjectRelation('Personne')
 
-_euser = import_schema('base').CWUser
-_euser.__relations__[0].fulltextindexed = True
+from cubicweb.schemas.base import CWUser
+CWUser.get_relations('login').next().fulltextindexed = True
 
 class Note(EntityType):
     date = String(maxsize=10)
@@ -131,14 +134,14 @@
         'delete': ('managers', RRQLExpression('O owned_by U')),
         }
 
-class para(AttributeRelationType):
+class para(RelationType):
     permissions = {
         'read':   ('managers', 'users', 'guests'),
         'add':    ('managers', ERQLExpression('X in_state S, S name "todo"')),
         'delete': ('managers', ERQLExpression('X in_state S, S name "todo"')),
         }
 
-class test(AttributeRelationType):
+class test(RelationType):
     permissions = {'read': ('managers', 'users', 'guests'),
                    'delete': ('managers',),
                    'add': ('managers',)}
--- a/server/test/unittest_hookhelper.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/unittest_hookhelper.py	Fri Jul 31 14:25:30 2009 +0200
@@ -41,7 +41,7 @@
         from cubicweb.server import hooks, schemahooks
         session = self.session
         op1 = hooks.DelayedDeleteOp(session)
-        op2 = schemahooks.DelErdefOp(session)
+        op2 = schemahooks.DeleteRelationDefOp(session)
         # equivalent operation generated by op2 but replace it here by op3 so we
         # can check the result...
         op3 = schemahooks.UpdateSchemaOp(session)
--- a/server/test/unittest_hooks.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/unittest_hooks.py	Fri Jul 31 14:25:30 2009 +0200
@@ -6,9 +6,13 @@
 """
 
 from logilab.common.testlib import TestCase, unittest_main
+
+from datetime import datetime
+
+from cubicweb import (ConnectionError, RepositoryError, ValidationError,
+                      AuthenticationError, BadConnectionId)
 from cubicweb.devtools.apptest import RepositoryBasedTC, get_versions
 
-from cubicweb import ConnectionError, RepositoryError, ValidationError, AuthenticationError, BadConnectionId
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.repository import Repository
 
@@ -265,8 +269,8 @@
         self.failIf(schema.has_entity('Societe2'))
         self.failIf(schema.has_entity('concerne2'))
         # schema should be update on insertion (after commit)
-        self.execute('INSERT CWEType X: X name "Societe2", X description "", X meta FALSE, X final FALSE')
-        self.execute('INSERT CWRType X: X name "concerne2", X description "", X meta FALSE, X final FALSE, X symetric FALSE')
+        self.execute('INSERT CWEType X: X name "Societe2", X description "", X final FALSE')
+        self.execute('INSERT CWRType X: X name "concerne2", X description "", X final FALSE, X symetric FALSE')
         self.failIf(schema.has_entity('Societe2'))
         self.failIf(schema.has_entity('concerne2'))
         self.execute('SET X read_permission G WHERE X is CWEType, X name "Societe2", G is CWGroup')
@@ -614,5 +618,39 @@
         self.failUnless(cu.execute("INSERT Note X: X type 'a', X in_state S WHERE S name 'todo'"))
         cnx.commit()
 
+    def test_metadata_cwuri(self):
+        eid = self.execute('INSERT Note X')[0][0]
+        cwuri = self.execute('Any U WHERE X eid %s, X cwuri U' % eid)[0][0]
+        self.assertEquals(cwuri, self.repo.config['base-url'] + 'eid/%s' % eid)
+
+    def test_metadata_creation_modification_date(self):
+        _now = datetime.now()
+        eid = self.execute('INSERT Note X')[0][0]
+        creation_date, modification_date = self.execute('Any CD, MD WHERE X eid %s, '
+                                                        'X creation_date CD, '
+                                                        'X modification_date MD' % eid)[0]
+        self.assertEquals((creation_date - _now).seconds, 0)
+        self.assertEquals((modification_date - _now).seconds, 0)
+
+    def test_metadata__date(self):
+        _now = datetime.now()
+        eid = self.execute('INSERT Note X')[0][0]
+        creation_date = self.execute('Any D WHERE X eid %s, X creation_date D' % eid)[0][0]
+        self.assertEquals((creation_date - _now).seconds, 0)
+
+    def test_metadata_created_by(self):
+        eid = self.execute('INSERT Note X')[0][0]
+        self.commit() # fire operations
+        rset = self.execute('Any U WHERE X eid %s, X created_by U' % eid)
+        self.assertEquals(len(rset), 1) # make sure we have only one creator
+        self.assertEquals(rset[0][0], self.session.user.eid)
+
+    def test_metadata_owned_by(self):
+        eid = self.execute('INSERT Note X')[0][0]
+        self.commit() # fire operations
+        rset = self.execute('Any U WHERE X eid %s, X owned_by U' % eid)
+        self.assertEquals(len(rset), 1) # make sure we have only one owner
+        self.assertEquals(rset[0][0], self.session.user.eid)
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_schemaserial.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/unittest_schemaserial.py	Fri Jul 31 14:25:30 2009 +0200
@@ -23,15 +23,15 @@
     def test_eschema2rql1(self):
         self.assertListEquals(list(eschema2rql(schema.eschema('CWAttribute'))),
                               [
-            ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
-             {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema',
-              'meta': True, 'name': u'CWAttribute', 'final': False})
+            ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s',
+             {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the instance schema',
+              'name': u'CWAttribute', 'final': False})
             ])
 
     def test_eschema2rql2(self):
         self.assertListEquals(list(eschema2rql(schema.eschema('String'))), [
-                ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
-                 {'description': u'', 'final': True, 'meta': True, 'name': u'String'})])
+                ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s',
+                 {'description': u'', 'final': True, 'name': u'String'})])
 
     def test_eschema2rql_specialization(self):
         self.assertListEquals(list(specialize2rql(schema)),
@@ -44,8 +44,8 @@
     def test_rschema2rql1(self):
         self.assertListEquals(list(rschema2rql(schema.rschema('relation_type'))),
                              [
-            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
-             {'description': u'link a relation definition to its relation type', 'meta': True, 'symetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}),
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s',
+             {'description': u'link a relation definition to its relation type', 'symetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}),
 
             ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
              {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'CWRType',
@@ -63,7 +63,7 @@
     def test_rschema2rql2(self):
         self.assertListEquals(list(rschema2rql(schema.rschema('add_permission'))),
                               [
-            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'meta': True, 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
 
             ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
              {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWEType'}),
@@ -79,8 +79,8 @@
     def test_rschema2rql3(self):
         self.assertListEquals(list(rschema2rql(schema.rschema('cardinality'))),
                              [
-            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
-             {'description': u'', 'meta': False, 'symetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}),
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s',
+             {'description': u'', 'symetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}),
 
             ('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
              {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'CWRelation'}),
@@ -100,31 +100,31 @@
 
     def test_updateeschema2rql1(self):
         self.assertListEquals(list(updateeschema2rql(schema.eschema('CWAttribute'))),
-                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
-                                {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', 'meta': True, 'et': 'CWAttribute', 'final': False, 'name': u'CWAttribute'}),
+                              [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
+                                {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the instance schema', 'et': 'CWAttribute', 'final': False, 'name': u'CWAttribute'}),
                                ])
 
     def test_updateeschema2rql2(self):
         self.assertListEquals(list(updateeschema2rql(schema.eschema('String'))),
-                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
-                                {'description': u'', 'meta': True, 'et': 'String', 'final': True, 'name': u'String'})
+                              [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
+                                {'description': u'', 'et': 'String', 'final': True, 'name': u'String'})
                                ])
 
     def test_updaterschema2rql1(self):
         self.assertListEquals(list(updaterschema2rql(schema.rschema('relation_type'))),
                              [
-            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
+            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
              {'rt': 'relation_type', 'symetric': False,
               'description': u'link a relation definition to its relation type',
-              'meta': True, 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'})
+              'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'})
             ])
 
     def test_updaterschema2rql2(self):
         expected = [
-            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
+            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
              {'rt': 'add_permission', 'symetric': False,
               'description': u'core relation giving to a group the permission to add an entity or relation type',
-              'meta': True, 'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'})
+              'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'})
             ]
         for i, (rql, args) in enumerate(updaterschema2rql(schema.rschema('add_permission'))):
             yield self.assertEquals, (rql, args), expected[i]
--- a/server/test/unittest_security.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/server/test/unittest_security.py	Fri Jul 31 14:25:30 2009 +0200
@@ -499,7 +499,7 @@
         self.assertRaises(Unauthorized,
                           self.schema['Affaire'].check_perm, session, 'update', eid)
         cu = cnx.cursor()
-        cu.execute('SET X in_state S WHERE X ref "ARCT01", S name "abort"')
+        cu.execute('SET X in_state S WHERE X ref "ARCT01", S name "ben non"')
         cnx.commit()
         # though changing a user state (even logged user) is reserved to managers
         rql = u"SET X in_state S WHERE X eid %(x)s, S name 'deactivated'"
@@ -508,5 +508,25 @@
         # from the current state but Unauthorized if it exists but user can't pass it
         self.assertRaises(ValidationError, cu.execute, rql, {'x': cnx.user(self.current_session()).eid}, 'x')
 
+    def test_trinfo_security(self):
+        aff = self.execute('INSERT Affaire X: X ref "ARCT01"').get_entity(0, 0)
+        self.commit()
+        # can change tr info comment
+        self.execute('SET TI comment %(c)s WHERE TI wf_info_for X, X ref "ARCT01"',
+                     {'c': u'creation'})
+        self.commit()
+        self.assertEquals(aff.latest_trinfo().comment, 'creation')
+        # but not from_state/to_state
+        self.execute('SET X in_state S WHERE X ref "ARCT01", S name "ben non"')
+        self.commit()
+        aff.clear_related_cache('wf_info_for', role='object')
+        trinfo = aff.latest_trinfo()
+        self.assertRaises(Unauthorized,
+                          self.execute, 'SET TI from_state S WHERE TI eid %(ti)s, S name "ben non"',
+                          {'ti': trinfo.eid}, 'ti')
+        self.assertRaises(Unauthorized,
+                          self.execute, 'SET TI to_state S WHERE TI eid %(ti)s, S name "pitetre"',
+                          {'ti': trinfo.eid}, 'ti')
+
 if __name__ == '__main__':
     unittest_main()
--- a/sobjects/hooks.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/sobjects/hooks.py	Fri Jul 31 14:25:30 2009 +0200
@@ -7,11 +7,32 @@
 """
 __docformat__ = "restructuredtext en"
 
+from datetime import datetime
+
+from cubicweb import RepositoryError
 from cubicweb.common.uilib import soup2xhtml
 from cubicweb.server.hooksmanager import Hook
 from cubicweb.server.pool import PreCommitOperation
 
 
+class SetModificationDateOnStateChange(Hook):
+    """update entity's modification date after changing its state"""
+    events = ('after_add_relation',)
+    accepts = ('in_state',)
+
+    def call(self, session, fromeid, rtype, toeid):
+        if fromeid in session.transaction_data.get('neweids', ()):
+            # new entity, not needed
+            return
+        entity = session.entity_from_eid(fromeid)
+        try:
+            entity.set_attributes(modification_date=datetime.now())
+        except RepositoryError, ex:
+            # usually occurs if entity is coming from a read-only source
+            # (eg ldap user)
+            self.warning('cant change modification date for %s: %s', entity, ex)
+
+
 class AddUpdateCWUserHook(Hook):
     """ensure user logins are stripped"""
     events = ('before_add_entity', 'before_update_entity',)
--- a/sobjects/notification.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/sobjects/notification.py	Fri Jul 31 14:25:30 2009 +0200
@@ -14,7 +14,7 @@
 try:
     from socket import gethostname
 except ImportError:
-    def gethostname():
+    def gethostname(): # gae
         return 'XXX'
 
 from logilab.common.textutils import normalize_text
@@ -62,7 +62,7 @@
 class RenderAndSendNotificationView(PreCommitOperation):
     """delay rendering of notification view until precommit"""
     def precommit_event(self):
-        if self.view.rset[0][0] in self.session.transaction_data.get('pendingeids', ()):
+        if self.view.rset and self.view.rset[0][0] in self.session.transaction_data.get('pendingeids', ()):
             return # entity added and deleted in the same transaction
         self.view.render_and_send(**getattr(self, 'viewargs', {}))
 
@@ -76,8 +76,8 @@
             return
         rset = entity.related('wf_info_for')
         try:
-            view = session.vreg.select_view('notif_status_change',
-                                            session, rset, row=0)
+            view = session.vreg.select('views', 'notif_status_change',
+                                       session, rset=rset, row=0)
         except RegistryException:
             return
         comment = entity.printable_value('comment', format='text/plain')
@@ -100,7 +100,7 @@
         rset = session.eid_rset(fromeid)
         vid = 'notif_%s_%s' % (self.event,  rtype)
         try:
-            view = session.vreg.select_view(vid, session, rset, row=0)
+            view = session.vreg.select('views', vid, session, rset=rset, row=0)
         except RegistryException:
             return
         RenderAndSendNotificationView(session, view=view)
@@ -117,7 +117,7 @@
         rset = entity.as_rset()
         vid = 'notif_%s' % self.event
         try:
-            view = session.vreg.select_view(vid, session, rset, row=0)
+            view = session.vreg.select('views', vid, session, rset=rset, row=0)
         except RegistryException:
             return
         RenderAndSendNotificationView(session, view=view)
@@ -133,14 +133,17 @@
     * set a content attribute to define the content of the email (unless you
       override call)
     """
+    # XXX refactor this class to work with len(rset) > 1
+
     msgid_timestamp = True
 
     def recipients(self):
-        finder = self.vreg.select_component('recipients_finder', self.req, self.rset)
+        finder = self.vreg.select('components', 'recipients_finder', self.req,
+                                  rset=self.rset)
         return finder.recipients()
 
     def subject(self):
-        entity = self.entity(0, 0)
+        entity = self.entity(self.row or 0, self.col or 0)
         subject = self.req._(self.message)
         etype = entity.dc_type()
         eid = entity.eid
@@ -153,7 +156,7 @@
         return self.req.actual_session().user.login
 
     def context(self, **kwargs):
-        entity = self.entity(0, 0)
+        entity = self.entity(self.row or 0, self.col or 0)
         for key, val in kwargs.iteritems():
             if val and isinstance(val, unicode) and val.strip():
                kwargs[key] = self.req._(val)
@@ -183,15 +186,19 @@
                  DeprecationWarning, stacklevel=1)
             lang = self.vreg.property_value('ui.language')
             recipients = zip(recipients, repeat(lang))
-        entity = self.entity(0, 0)
-        # if the view is using timestamp in message ids, no way to reference
-        # previous email
-        if not self.msgid_timestamp:
-            refs = [self.construct_message_id(eid)
-                    for eid in entity.notification_references(self)]
+        if self.rset is not None:
+            entity = self.entity(self.row or 0, self.col or 0)
+            # if the view is using timestamp in message ids, no way to reference
+            # previous email
+            if not self.msgid_timestamp:
+                refs = [self.construct_message_id(eid)
+                        for eid in entity.notification_references(self)]
+            else:
+                refs = ()
+            msgid = self.construct_message_id(entity.eid)
         else:
             refs = ()
-        msgid = self.construct_message_id(entity.eid)
+            msgid = None
         userdata = self.req.user_data()
         origlang = self.req.lang
         for emailaddr, lang in recipients:
@@ -277,7 +284,7 @@
 """
 
     def context(self, **kwargs):
-        entity = self.entity(0, 0)
+        entity = self.entity(self.row or 0, self.col or 0)
         content = entity.printable_value(self.content_attr, format='text/plain')
         if content:
             contentformat = getattr(entity, self.content_attr + '_format', 'text/rest')
@@ -285,7 +292,7 @@
         return super(ContentAddedView, self).context(content=content, **kwargs)
 
     def subject(self):
-        entity = self.entity(0, 0)
+        entity = self.entity(self.row or 0, self.col or 0)
         return  u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
                                   entity.eid, self.user_login())
 
--- a/sobjects/supervising.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/sobjects/supervising.py	Fri Jul 31 14:25:30 2009 +0200
@@ -217,8 +217,8 @@
     of changes
     """
     def _get_view(self):
-        return self.session.vreg.select_component('supervision_notif',
-                                                  self.session, None)
+        return self.session.vreg.select('components', 'supervision_notif',
+                                        self.session)
 
     def _prepare_email(self):
         session = self.session
--- a/sobjects/test/unittest_notification.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/sobjects/test/unittest_notification.py	Fri Jul 31 14:25:30 2009 +0200
@@ -56,7 +56,8 @@
         self.execute('INSERT CWProperty X: X pkey "ui.language", X value "fr", X for_user U '
                      'WHERE U eid %(x)s', {'x': urset[0][0]})
         self.commit() # commit so that admin get its properties updated
-        finder = self.vreg.select_component('recipients_finder', self.request(), urset)
+        finder = self.vreg.select('components', 'recipients_finder', self.request(),
+                                  rset=urset)
         self.set_option('default-recipients-mode', 'none')
         self.assertEquals(finder.recipients(), [])
         self.set_option('default-recipients-mode', 'users')
@@ -73,7 +74,7 @@
         u = self.create_user('toto', req=req)
         assert u.req
         self.execute('SET X in_state S WHERE X eid %s, S name "deactivated"' % u.eid)
-        v = self.vreg.select_view('notif_status_change', req, u.rset, row=0)
+        v = self.vreg.select('views', 'notif_status_change', req, u.rset, row=0)
         content = v.render(row=0, comment='yeah',
                            previous_state='activated',
                            current_state='deactivated')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spa2rql.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,207 @@
+"""SPARQL -> RQL translator
+
+:organization: Logilab
+:copyright: 2009 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
+"""
+from logilab.common import make_domains
+from rql import TypeResolverException
+from fyzz.yappsparser import parse
+from fyzz import ast
+
+from cubicweb.xy import xy
+
+
+class UnsupportedQuery(Exception): pass
+
+def order_limit_offset(sparqlst):
+    addons = ''
+    if sparqlst.orderby:
+        sortterms = ', '.join('%s %s' % (var.name.upper(), ascdesc.upper())
+                              for var, ascdesc in sparqlst.orderby)
+        addons += ' ORDERBY %s' % sortterms
+    if sparqlst.limit:
+        addons += ' LIMIT %s' % sparqlst.limit
+    if sparqlst.offset:
+        addons += ' OFFSET %s' % sparqlst.offset
+    return addons
+
+
+class QueryInfo(object):
+    """wrapper class containing necessary information to generate a RQL query
+    from a sparql syntax tree
+    """
+    def __init__(self, sparqlst):
+        self.sparqlst = sparqlst
+        if sparqlst.selected == ['*']:
+            self.selection = [var.upper() for var in sparqlst.variables]
+        else:
+            self.selection = [var.name.upper() for var in sparqlst.selected]
+        self.possible_types = {}
+        self.infer_types_info = []
+        self.union_params = []
+        self.restrictions = []
+        self.literals = {}
+        self._litcount = 0
+
+    def add_literal(self, value):
+        key = chr(ord('a') + self._litcount)
+        self._litcount += 1
+        self.literals[key] = value
+        return key
+
+    def set_possible_types(self, var, varpossibletypes):
+        """set/restrict possible types for the given variable.
+
+        :return: True if something changed, else false.
+        :raise: TypeResolverException if no more type allowed
+        """
+        varpossibletypes = set(varpossibletypes)
+        try:
+            ctypes = self.possible_types[var]
+            nbctypes = len(ctypes)
+            ctypes &= varpossibletypes
+            if not ctypes:
+                raise TypeResolverException()
+            return len(ctypes) != nbctypes
+        except KeyError:
+            self.possible_types[var] = varpossibletypes
+            return True
+
+    def infer_types(self):
+        # XXX should use something similar to rql.analyze for proper type inference
+        modified = True
+        # loop to infer types until nothing changed
+        while modified:
+            modified = False
+            for yams_predicates, subjvar, obj in self.infer_types_info:
+                nbchoices = len(yams_predicates)
+                # get possible types for the subject variable, according to the
+                # current predicate
+                svptypes = set(s for s, r, o in yams_predicates)
+                if not '*' in svptypes:
+                    if self.set_possible_types(subjvar, svptypes):
+                        modified = True
+                # restrict predicates according to allowed subject var types
+                if subjvar in self.possible_types:
+                    yams_predicates = [(s, r, o) for s, r, o in yams_predicates
+                                       if s == '*' or s in self.possible_types[subjvar]]
+                if isinstance(obj, ast.SparqlVar):
+                    # make a valid rql var name
+                    objvar = obj.name.upper()
+                    # get possible types for the object variable, according to
+                    # the current predicate
+                    ovptypes = set(o for s, r, o in yams_predicates)
+                    if not '*' in ovptypes:
+                        if self.set_possible_types(objvar, ovptypes):
+                            modified = True
+                    # restrict predicates according to allowed object var types
+                    if objvar in self.possible_types:
+                        yams_predicates = [(s, r, o) for s, r, o in yams_predicates
+                                           if o == '*' or o in self.possible_types[objvar]]
+                # ensure this still make sense
+                if not yams_predicates:
+                    raise TypeResolverException()
+                if len(yams_predicates) != nbchoices:
+                    modified = True
+
+    def build_restrictions(self):
+        # now, for each predicate
+        for yams_predicates, subjvar, obj in self.infer_types_info:
+            rel = yams_predicates[0]
+            # if there are several yams relation type equivalences, we will have
+            # to generate several unioned rql queries
+            for s, r, o in yams_predicates[1:]:
+                if r != rel[1]:
+                    self.union_params.append((yams_predicates, subjvar, obj))
+                    break
+            # else we can simply add it to base rql restrictions
+            else:
+                restr = self.build_restriction(subjvar, rel[1], obj)
+                self.restrictions.append(restr)
+
+    def build_restriction(self, subjvar, rtype, obj):
+        if isinstance(obj, ast.SparqlLiteral):
+            key = self.add_literal(obj.value)
+            objvar = '%%(%s)s' % key
+        else:
+            assert isinstance(obj, ast.SparqlVar)
+            # make a valid rql var name
+            objvar = obj.name.upper()
+        # else we can simply add it to base rql restrictions
+        return '%s %s %s' % (subjvar, rtype, objvar)
+
+    def finalize(self):
+        """return corresponding rql query (string) / args (dict)"""
+        for varname, ptypes in self.possible_types.iteritems():
+            if len(ptypes) == 1:
+                self.restrictions.append('%s is %s' % (varname, iter(ptypes).next()))
+        unions = []
+        for releq, subjvar, obj in self.union_params:
+            thisunions = []
+            for st, rt, ot in releq:
+                thisunions.append([self.build_restriction(subjvar, rt, obj)])
+                if st != '*':
+                    thisunions[-1].append('%s is %s' % (subjvar, st))
+                if isinstance(obj, ast.SparqlVar) and ot != '*':
+                    objvar = obj.name.upper()
+                    thisunions[-1].append('%s is %s' % (objvar, objvar))
+            if not unions:
+                unions = thisunions
+            else:
+                unions = zip(*make_domains([unions, thisunions]))
+        selection = 'Any ' + ', '.join(self.selection)
+        sparqlst = self.sparqlst
+        if sparqlst.distinct:
+            selection = 'DISTINCT ' + selection
+        if unions:
+            baserql = '%s WHERE %s' % (selection, ', '.join(self.restrictions))
+            rqls = ['(%s, %s)' % (baserql, ', '.join(unionrestrs))
+                    for unionrestrs in unions]
+            rql = ' UNION '.join(rqls)
+            if sparqlst.orderby or sparqlst.limit or sparqlst.offset:
+                rql = '%s%s WITH %s BEING (%s)' % (
+                    selection, order_limit_offset(sparqlst),
+                    ', '.join(self.selection), rql)
+        else:
+            rql = '%s%s WHERE %s' % (selection, order_limit_offset(sparqlst),
+                                      ', '.join(self.restrictions))
+        return rql, self.literals
+
+
+class Sparql2rqlTranslator(object):
+    def __init__(self, yschema):
+        self.yschema = yschema
+
+    def translate(self, sparql):
+        sparqlst = parse(sparql)
+        if sparqlst.type != 'select':
+            raise UnsupportedQuery()
+        qi = QueryInfo(sparqlst)
+        for subj, predicate, obj in sparqlst.where:
+            if not isinstance(subj, ast.SparqlVar):
+                raise UnsupportedQuery()
+            # make a valid rql var name
+            subjvar = subj.name.upper()
+            if predicate == ('', 'a'):
+                # special 'is' relation
+                if not isinstance(obj, tuple):
+                    raise UnsupportedQuery()
+                # restrict possible types for the subject variable
+                qi.set_possible_types(
+                    subjvar, xy.yeq(':'.join(obj), isentity=True))
+            else:
+                # 'regular' relation (eg not 'is')
+                if not isinstance(predicate, tuple):
+                    raise UnsupportedQuery()
+                # list of 3-uple
+                #   (yams etype (subject), yams rtype, yams etype (object))
+                # where subject / object entity type may '*' if not specified
+                yams_predicates = xy.yeq(':'.join(predicate))
+                qi.infer_types_info.append((yams_predicates, subjvar, obj))
+                if not isinstance(obj, (ast.SparqlLiteral, ast.SparqlVar)):
+                    raise UnsupportedQuery()
+        qi.infer_types()
+        qi.build_restrictions()
+        return qi
--- a/test/unittest_cwconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/test/unittest_cwconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -8,7 +8,6 @@
 import sys
 import os
 from os.path import dirname, join, abspath
-from tempfile import mktemp
 
 from logilab.common.testlib import TestCase, unittest_main
 from logilab.common.changelog import Version
--- a/test/unittest_cwctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/test/unittest_cwctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -13,9 +13,9 @@
 if os.environ.get('APYCOT_ROOT'):
     root = os.environ['APYCOT_ROOT']
     CUBES_DIR = '%s/local/share/cubicweb/cubes/' % root
-    os.environ['CW_CUBES'] = CUBES_DIR
+    os.environ['CW_CUBES_PATH'] = CUBES_DIR
     REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
-    os.environ['CW_REGISTRY_DIR'] = REGISTRY_DIR
+    os.environ['CW_INSTANCES_DIR'] = REGISTRY_DIR
 
 from cubicweb.cwconfig import CubicWebConfiguration
 CubicWebConfiguration.load_cwctl_plugins()
--- a/test/unittest_schema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/test/unittest_schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -150,7 +150,7 @@
                              'CWCache', 'CWConstraint', 'CWConstraintType', 'CWEType',
                              'CWAttribute', 'CWGroup', 'EmailAddress', 'CWRelation',
                              'CWPermission', 'CWProperty', 'CWRType', 'CWUser',
-                             'File', 'Float', 'Image', 'Int', 'Interval', 'Note',
+                             'ExternalUri', 'File', 'Float', 'Image', 'Int', 'Interval', 'Note',
                              'Password', 'Personne',
                              'RQLExpression',
                              'Societe', 'State', 'String', 'SubNote', 'Tag', 'Time',
@@ -163,7 +163,7 @@
 
                               'cardinality', 'comment', 'comment_format',
                               'composite', 'condition', 'connait', 'constrained_by', 'content',
-                              'content_format', 'created_by', 'creation_date', 'cstrtype',
+                              'content_format', 'created_by', 'creation_date', 'cstrtype', 'cwuri',
 
                               'data', 'data_encoding', 'data_format', 'defaultval', 'delete_permission',
                               'description', 'description_format', 'destination_state',
@@ -179,7 +179,7 @@
 
                               'label', 'last_login_time', 'login',
 
-                              'mainvars', 'meta', 'modification_date',
+                              'mainvars', 'modification_date',
 
                               'name', 'nom',
 
@@ -193,7 +193,7 @@
 
                               'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type',
 
-                              'upassword', 'update_permission', 'use_email',
+                              'upassword', 'update_permission', 'uri', 'use_email',
 
                               'value',
 
@@ -203,7 +203,7 @@
 
         eschema = schema.eschema('CWUser')
         rels = sorted(str(r) for r in eschema.subject_relations())
-        self.assertListEquals(rels, ['created_by', 'creation_date', 'eid',
+        self.assertListEquals(rels, ['created_by', 'creation_date', 'cwuri', 'eid',
                                      'evaluee', 'firstname', 'has_text', 'identity',
                                      'in_group', 'in_state', 'is',
                                      'is_instance_of', 'last_login_time',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_spa2rql.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,180 @@
+from logilab.common.testlib import TestCase, unittest_main
+from cubicweb.devtools import TestServerConfiguration
+from cubicweb.xy import xy
+from cubicweb.spa2rql import Sparql2rqlTranslator
+
+xy.add_equivalence('Project', 'doap:Project')
+xy.add_equivalence('Project creation_date', 'doap:Project doap:created')
+xy.add_equivalence('Project name', 'doap:Project doap:name')
+
+
+config = TestServerConfiguration('data')
+config.bootstrap_cubes()
+schema = config.load_schema()
+
+
+class XYTC(TestCase):
+    def setUp(self):
+        self.tr = Sparql2rqlTranslator(schema)
+
+    def _test(self, sparql, rql, args={}):
+        qi = self.tr.translate(sparql)
+        self.assertEquals(qi.finalize(), (rql, args))
+
+    def XXX_test_base_01(self):
+        self._test('SELECT * WHERE { }', 'Any X')
+
+
+    def test_base_is(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?project
+    WHERE  {
+      ?project a doap:Project;
+    }''', 'Any PROJECT WHERE PROJECT is Project')
+
+
+    def test_base_attr_sel(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?created
+    WHERE  {
+      ?project a doap:Project;
+              doap:created ?created.
+    }''', 'Any CREATED WHERE PROJECT creation_date CREATED, PROJECT is Project')
+
+
+    def test_base_attr_sel_distinct(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT DISTINCT ?name
+    WHERE  {
+      ?project a doap:Project;
+              doap:name ?name.
+    }''', 'DISTINCT Any NAME WHERE PROJECT name NAME, PROJECT is Project')
+
+
+    def test_base_attr_sel_reduced(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT REDUCED ?name
+    WHERE  {
+      ?project a doap:Project;
+              doap:name ?name.
+    }''', 'Any NAME WHERE PROJECT name NAME, PROJECT is Project')
+
+
+    def test_base_attr_sel_limit_offset(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?name
+    WHERE  {
+      ?project a doap:Project;
+              doap:name ?name.
+    }
+    LIMIT 20''', 'Any NAME LIMIT 20 WHERE PROJECT name NAME, PROJECT is Project')
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?name
+    WHERE  {
+      ?project a doap:Project;
+              doap:name ?name.
+    }
+    LIMIT 20 OFFSET 10''', 'Any NAME LIMIT 20 OFFSET 10 WHERE PROJECT name NAME, PROJECT is Project')
+
+
+    def test_base_attr_sel_orderby(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?name
+    WHERE  {
+      ?project a doap:Project;
+              doap:name ?name;
+              doap:created ?created.
+    }
+    ORDER BY ?name DESC(?created)''', 'Any NAME ORDERBY NAME ASC, CREATED DESC WHERE PROJECT name NAME, PROJECT creation_date CREATED, PROJECT is Project')
+
+
+    def test_base_any_attr_sel(self):
+        self._test('''
+    PREFIX dc: <http://purl.org/dc/elements/1.1/>
+    SELECT ?x ?cd
+    WHERE  {
+      ?x dc:date ?cd;
+    }''', 'Any X, CD WHERE X creation_date CD')
+
+
+    def test_base_any_attr_sel_amb(self):
+        xy.add_equivalence('Version publication_date', 'doap:Version dc:date')
+        try:
+            self._test('''
+    PREFIX dc: <http://purl.org/dc/elements/1.1/>
+    SELECT ?x ?cd
+    WHERE  {
+      ?x dc:date ?cd;
+    }''', '(Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version)')
+        finally:
+            xy.remove_equivalence('Version publication_date', 'doap:Version dc:date')
+
+
+    def test_base_any_attr_sel_amb_limit_offset(self):
+        xy.add_equivalence('Version publication_date', 'doap:Version dc:date')
+        try:
+            self._test('''
+    PREFIX dc: <http://purl.org/dc/elements/1.1/>
+    SELECT ?x ?cd
+    WHERE  {
+      ?x dc:date ?cd;
+    }
+    LIMIT 20 OFFSET 10''', 'Any X, CD LIMIT 20 OFFSET 10 WITH X, CD BEING ((Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version))')
+        finally:
+            xy.remove_equivalence('Version publication_date', 'doap:Version dc:date')
+
+
+    def test_base_any_attr_sel_amb_orderby(self):
+        xy.add_equivalence('Version publication_date', 'doap:Version dc:date')
+        try:
+            self._test('''
+    PREFIX dc: <http://purl.org/dc/elements/1.1/>
+    SELECT ?x ?cd
+    WHERE  {
+      ?x dc:date ?cd;
+    }
+    ORDER BY DESC(?cd)''', 'Any X, CD ORDERBY CD DESC WITH X, CD BEING ((Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version))')
+        finally:
+            xy.remove_equivalence('Version publication_date', 'doap:Version dc:date')
+
+
+    def test_restr_attr(self):
+        self._test('''
+    PREFIX doap: <http://usefulinc.com/ns/doap#>
+    SELECT ?project
+    WHERE  {
+      ?project a doap:Project;
+              doap:name "cubicweb".
+    }''', 'Any PROJECT WHERE PROJECT name %(a)s, PROJECT is Project', {'a': 'cubicweb'})
+
+# # Two elements in the group
+# PREFIX :  <http://example.org/ns#>
+# SELECT *
+# { :p :q :r  OPTIONAL { :a :b :c }
+#   :p :q :r  OPTIONAL { :a :b :c }
+# }
+
+# PREFIX : <http://example.org/ns#>
+# SELECT *
+# {
+#   { ?s ?p ?o } UNION { ?a ?b ?c }
+# }
+
+# PREFIX dob: <http://placetime.com/interval/gregorian/1977-01-18T04:00:00Z/P>
+# PREFIX time: <http://www.ai.sri.com/daml/ontologies/time/Time.daml#>
+# PREFIX dc: <http://purl.org/dc/elements/1.1/>
+# SELECT ?desc
+# WHERE  {
+#   dob:1D a time:ProperInterval;
+#          dc:description ?desc.
+# }
+
+if __name__ == '__main__':
+    unittest_main()
--- a/toolsutils.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/toolsutils.py	Fri Jul 31 14:25:30 2009 +0200
@@ -140,7 +140,7 @@
     chmod(filepath, 0600)
 
 def read_config(config_file):
-    """read the application configuration from a file and return it as a
+    """read the instance configuration from a file and return it as a
     dictionnary
 
     :type config_file: str
--- a/utils.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/utils.py	Fri Jul 31 14:25:30 2009 +0200
@@ -255,10 +255,10 @@
         # 1/ variable declaration if any
         if self.jsvars:
             from simplejson import dumps
-            w(u'<script type="text/javascript">\n')
+            w(u'<script type="text/javascript"><!--//--><![CDATA[//><!--\n')
             for var, value in self.jsvars:
                 w(u'%s = %s;\n' % (var, dumps(value)))
-            w(u'</script>\n')
+            w(u'//--><!]]></script>\n')
         # 2/ css files
         for cssfile, media in self.cssfiles:
             w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
--- a/view.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/view.py	Fri Jul 31 14:25:30 2009 +0200
@@ -195,10 +195,27 @@
         necessary for non linkable views, but a default implementation
         is provided anyway.
         """
-        try:
-            return self.build_url(vid=self.id, rql=self.req.form['rql'])
-        except KeyError:
-            return self.build_url(vid=self.id)
+        rset = self.rset
+        if rset is None:
+            return self.build_url('view', vid=self.id)
+        coltypes = rset.column_types(0)
+        if len(coltypes) == 1:
+            etype = iter(coltypes).next()
+            if not self.schema.eschema(etype).is_final():
+                if len(rset) == 1:
+                    entity = rset.get_entity(0, 0)
+                    return entity.absolute_url(vid=self.id)
+            # don't want to generate /<etype> url if there is some restriction
+            # on something else than the entity type
+            restr = rset.syntax_tree().children[0].where
+            # XXX norestriction is not correct here. For instance, in cases like
+            # "Any P,N WHERE P is Project, P name N" norestriction should equal
+            # True
+            norestriction = (isinstance(restr, nodes.Relation) and
+                             restr.is_types_restriction())
+            if norestriction:
+                return self.build_url(etype.lower(), vid=self.id)
+        return self.build_url('view', rql=rset.printable_rql(), vid=self.id)
 
     def set_request_content_type(self):
         """set the content type returned by this view"""
@@ -313,10 +330,6 @@
 
     category = 'startupview'
 
-    def url(self):
-        """return the url associated with this view. We can omit rql here"""
-        return self.build_url('view', vid=self.id)
-
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
@@ -334,7 +347,7 @@
 
     default_rql = None
 
-    def __init__(self, req, rset, **kwargs):
+    def __init__(self, req, rset=None, **kwargs):
         super(EntityStartupView, self).__init__(req, rset, **kwargs)
         if rset is None:
             # this instance is not in the "entityview" category
@@ -354,14 +367,6 @@
         for i in xrange(len(rset)):
             self.wview(self.id, rset, row=i, **kwargs)
 
-    def url(self):
-        """return the url associated with this view. We can omit rql if we are
-        on a result set on which we do not apply.
-        """
-        if self.rset is None:
-            return self.build_url(vid=self.id)
-        return super(EntityStartupView, self).url()
-
 
 class AnyRsetView(View):
     """base class for views applying on any non empty result sets"""
--- a/vregistry.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/vregistry.py	Fri Jul 31 14:25:30 2009 +0200
@@ -22,10 +22,11 @@
 __docformat__ = "restructuredtext en"
 
 import sys
+import types
 from os import listdir, stat
 from os.path import dirname, join, realpath, split, isdir, exists
 from logging import getLogger
-import types
+from warnings import warn
 
 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods
 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject
@@ -169,25 +170,94 @@
         If no oid is given, return all objects in this registry
         """
         registry = self.registry(name)
-        if oid:
+        if oid is not None:
             try:
                 return registry[oid]
             except KeyError:
                 raise ObjectNotFound(oid), None, sys.exc_info()[-1]
-        else:
-            result = []
-            for objs in registry.values():
-                result += objs
-            return result
+        result = []
+        for objs in registry.values():
+            result += objs
+        return result
+
+    # dynamic selection methods ################################################
 
-    def object_by_id(self, registry, cid, *args, **kwargs):
-        """return the most specific component according to the resultset"""
-        objects = self[registry][cid]
+    def object_by_id(self, registry, oid, *args, **kwargs):
+        """return object in <registry>.<oid>
+
+        raise `ObjectNotFound` if not object with id <oid> in <registry>
+        raise `AssertionError` if there is more than one object there
+        """
+        objects = self.registry_objects(registry, oid)
         assert len(objects) == 1, objects
         return objects[0].selected(*args, **kwargs)
 
+    def select(self, registry, oid, *args, **kwargs):
+        """return the most specific object in <registry>.<oid> according to
+        the given context
+
+        raise `ObjectNotFound` if not object with id <oid> in <registry>
+        raise `NoSelectableObject` if not object apply
+        """
+        return self.select_best(self.registry_objects(registry, oid),
+                                *args, **kwargs)
+
+    def select_object(self, registry, oid, *args, **kwargs):
+        """return the most specific object in <registry>.<oid> according to
+        the given context, or None if no object apply
+        """
+        try:
+            return self.select(registry, oid, *args, **kwargs)
+        except (NoSelectableObject, ObjectNotFound):
+            return None
+
+    def possible_objects(self, registry, *args, **kwargs):
+        """return an iterator on possible objects in <registry> for the given
+        context
+        """
+        for vobjects in self.registry(registry).itervalues():
+            try:
+                yield self.select_best(vobjects, *args, **kwargs)
+            except NoSelectableObject:
+                continue
+
+    def select_best(self, vobjects, *args, **kwargs):
+        """return an instance of the most specific object according
+        to parameters
+
+        raise `NoSelectableObject` if not object apply
+        """
+        if len(args) > 1:
+            warn('only the request param can not be named when calling select',
+                 DeprecationWarning, stacklevel=3)
+        score, winners = 0, []
+        for vobject in vobjects:
+            vobjectscore = vobject.__select__(vobject, *args, **kwargs)
+            if vobjectscore > score:
+                score, winners = vobjectscore, [vobject]
+            elif vobjectscore > 0 and vobjectscore == score:
+                winners.append(vobject)
+        if not winners:
+            raise NoSelectableObject('args: %s\nkwargs: %s %s'
+                                     % (args, kwargs.keys(),
+                                        [repr(v) for v in vobjects]))
+        if len(winners) > 1:
+            if self.config.mode == 'installed':
+                self.error('select ambiguity, args: %s\nkwargs: %s %s',
+                           args, kwargs.keys(), [repr(v) for v in winners])
+            else:
+                raise Exception('select ambiguity, args: %s\nkwargs: %s %s'
+                                % (args, kwargs.keys(),
+                                   [repr(v) for v in winners]))
+        # return the result of the .selected method of the vobject
+        return winners[0].selected(*args, **kwargs)
+
     # methods for explicit (un)registration ###################################
 
+#     def clear(self, key):
+#         regname, oid = key.split('.')
+#         self[regname].pop(oid, None)
+
     def register_all(self, objects, modname, butclasses=()):
         for obj in objects:
             try:
@@ -259,52 +329,6 @@
                          replaced, obj)
         self.register(obj, registryname=registryname)
 
-    # dynamic selection methods ###############################################
-
-    def select(self, vobjects, *args, **kwargs):
-        """return an instance of the most specific object according
-        to parameters
-
-        raise NoSelectableObject if not object apply
-        """
-        score, winners = 0, []
-        for vobject in vobjects:
-            vobjectscore = vobject.__select__(vobject, *args, **kwargs)
-            if vobjectscore > score:
-                score, winners = vobjectscore, [vobject]
-            elif vobjectscore > 0 and vobjectscore == score:
-                winners.append(vobject)
-        if not winners:
-            raise NoSelectableObject('args: %s\nkwargs: %s %s'
-                                     % (args, kwargs.keys(),
-                                        [repr(v) for v in vobjects]))
-        if len(winners) > 1:
-            if self.config.mode == 'installed':
-                self.error('select ambiguity, args: %s\nkwargs: %s %s',
-                           args, kwargs.keys(), [repr(v) for v in winners])
-            else:
-                raise Exception('select ambiguity, args: %s\nkwargs: %s %s'
-                                % (args, kwargs.keys(),
-                                   [repr(v) for v in winners]))
-        winner = winners[0]
-        # return the result of the .selected method of the vobject
-        return winner.selected(*args, **kwargs)
-
-    def possible_objects(self, registry, *args, **kwargs):
-        """return an iterator on possible objects in a registry for this result set
-
-        actions returned are classes, not instances
-        """
-        for vobjects in self.registry(registry).values():
-            try:
-                yield self.select(vobjects, *args, **kwargs)
-            except NoSelectableObject:
-                continue
-
-    def select_object(self, registry, cid, *args, **kwargs):
-        """return the most specific component according to the resultset"""
-        return self.select(self.registry_objects(registry, cid), *args, **kwargs)
-
     # intialization methods ###################################################
 
     def init_registration(self, path, extrapath=None):
@@ -332,7 +356,7 @@
                 sys.path.remove(webdir)
         if CW_SOFTWARE_ROOT in sys.path:
             sys.path.remove(CW_SOFTWARE_ROOT)
-        # load views from each directory in the application's path
+        # load views from each directory in the instance's path
         filemods = self.init_registration(path, extrapath)
         change = False
         for filepath, modname in filemods:
--- a/web/application.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/application.py	Fri Jul 31 14:25:30 2009 +0200
@@ -10,22 +10,24 @@
 import sys
 from time import clock, time
 
+from logilab.common.deprecation import obsolete
+
 from rql import BadRQLQuery
 
-from cubicweb import set_log_methods
-from cubicweb import (ValidationError, Unauthorized, AuthenticationError,
-                      NoSelectableObject, RepositoryError)
-from cubicweb.cwvreg import CubicWebRegistry
-from cubicweb.web import (LOGGER, StatusResponse, DirectResponse, Redirect,
-                          NotFound, RemoteCallFailed, ExplicitLogin,
-                          InvalidSession, RequestError)
-from cubicweb.web.component import Component
+from cubicweb import set_log_methods, cwvreg
+from cubicweb import (
+    ValidationError, Unauthorized, AuthenticationError, NoSelectableObject,
+    RepositoryError)
+from cubicweb.web import LOGGER, component
+from cubicweb.web import (
+    StatusResponse, DirectResponse, Redirect, NotFound,
+    RemoteCallFailed, ExplicitLogin, InvalidSession, RequestError)
 
 # make session manager available through a global variable so the debug view can
 # print information about web session
 SESSION_MANAGER = None
 
-class AbstractSessionManager(Component):
+class AbstractSessionManager(component.Component):
     """manage session data associated to a session identifier"""
     id = 'sessionmanager'
 
@@ -39,8 +41,7 @@
         if self.session_time:
             assert self.cleanup_session_time < self.session_time
             assert self.cleanup_anon_session_time < self.session_time
-        self.authmanager = self.vreg.select_component('authmanager')
-        assert self.authmanager, 'no authentication manager found'
+        self.authmanager = self.vreg.select('components', 'authmanager')
 
     def clean_sessions(self):
         """cleanup sessions which has not been unused since a given amount of
@@ -88,7 +89,7 @@
         raise NotImplementedError()
 
 
-class AbstractAuthenticationManager(Component):
+class AbstractAuthenticationManager(component.Component):
     """authenticate user associated to a request and check session validity"""
     id = 'authmanager'
 
@@ -111,8 +112,7 @@
     SESSION_VAR = '__session'
 
     def __init__(self, appli):
-        self.session_manager = appli.vreg.select_component('sessionmanager')
-        assert self.session_manager, 'no session manager found'
+        self.session_manager = appli.vreg.select('components', 'sessionmanager')
         global SESSION_MANAGER
         SESSION_MANAGER = self.session_manager
         if not 'last_login_time' in appli.vreg.schema:
@@ -201,7 +201,7 @@
         raise Redirect(req.build_url(path, **args))
 
     def logout(self, req):
-        """logout from the application by cleaning the session and raising
+        """logout from the instance by cleaning the session and raising
         `AuthenticationError`
         """
         self.session_manager.close_session(req.cnx)
@@ -210,31 +210,19 @@
 
 
 class CubicWebPublisher(object):
-    """Central registry for the web application. This is one of the central
-    object in the web application, coupling dynamically loaded objects with
-    the application's schema and the application's configuration objects.
-
-    It specializes the VRegistry by adding some convenience methods to
-    access to stored objects. Currently we have the following registries
-    of objects known by the web application (library may use some others
-    additional registries):
-    * controllers, which are directly plugged into the application
-      object to handle request publishing
-    * views
-    * templates
-    * components
-    * actions
+    """the publisher is a singleton hold by the web frontend, and is responsible
+    to publish HTTP request.
     """
 
     def __init__(self, config, debug=None,
                  session_handler_fact=CookieSessionHandler,
                  vreg=None):
         super(CubicWebPublisher, self).__init__()
-        # connect to the repository and get application's schema
+        # connect to the repository and get instance's schema
         if vreg is None:
-            vreg = CubicWebRegistry(config, debug=debug)
+            vreg = cwvreg.CubicWebRegistry(config, debug=debug)
         self.vreg = vreg
-        self.info('starting web application from %s', config.apphome)
+        self.info('starting web instance from %s', config.apphome)
         self.repo = config.repository(vreg)
         if not vreg.initialized:
             self.config.init_cubes(self.repo.get_cubes())
@@ -251,7 +239,7 @@
             self.publish = self.main_publish
         # instantiate session and url resolving helpers
         self.session_handler = session_handler_fact(self)
-        self.url_resolver = vreg.select_component('urlpublisher')
+        self.url_resolver = vreg.select('components', 'urlpublisher')
 
     def connect(self, req):
         """return a connection for a logged user object according to existing
@@ -260,15 +248,6 @@
         """
         self.session_handler.set_session(req)
 
-    def select_controller(self, oid, req):
-        """return the most specific view according to the resultset"""
-        vreg = self.vreg
-        try:
-            return vreg.select(vreg.registry_objects('controllers', oid),
-                               req=req, appli=self)
-        except NoSelectableObject:
-            raise Unauthorized(req._('not authorized'))
-
     # publish methods #########################################################
 
     def log_publish(self, path, req):
@@ -293,6 +272,13 @@
             finally:
                 self._logfile_lock.release()
 
+    @obsolete("use vreg.select('controllers', ...)")
+    def select_controller(self, oid, req):
+        try:
+            return self.vreg.select('controllers', oid, req=req, appli=self)
+        except NoSelectableObject:
+            raise Unauthorized(req._('not authorized'))
+
     def main_publish(self, path, req):
         """method called by the main publisher to process <path>
 
@@ -317,7 +303,11 @@
         try:
             try:
                 ctrlid, rset = self.url_resolver.process(req, path)
-                controller = self.select_controller(ctrlid, req)
+                try:
+                    controller = self.vreg.select('controllers', ctrlid, req,
+                                                  appli=self)
+                except NoSelectableObject:
+                    raise Unauthorized(req._('not authorized'))
                 req.update_search_state()
                 result = controller.publish(rset=rset)
                 if req.cnx is not None:
@@ -385,7 +375,7 @@
             if tb:
                 req.data['excinfo'] = excinfo
             req.form['vid'] = 'error'
-            errview = self.vreg.select_view('error', req, None)
+            errview = self.vreg.select('views', 'error', req)
             template = self.main_template_id(req)
             content = self.vreg.main_template(req, template, view=errview)
         except:
@@ -400,7 +390,7 @@
 
     def notfound_content(self, req):
         req.form['vid'] = '404'
-        view = self.vreg.select_view('404', req, None)
+        view = self.vreg.select('views', '404', req)
         template = self.main_template_id(req)
         return self.vreg.main_template(req, template, view=view)
 
--- a/web/box.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/box.py	Fri Jul 31 14:25:30 2009 +0200
@@ -219,8 +219,8 @@
             return entity.unrelated(self.rtype, self.etype, get_role(self)).entities()
         # in other cases, use vocabulary functions
         entities = []
-        form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
-                                       row=self.row or 0)
+        form = self.vreg.select('forms', 'edition', self.req, rset=self.rset,
+                                row=self.row or 0)
         field = form.field_by_name(self.rtype, get_role(self), entity.e_schema)
         for _, eid in form.form_field_vocabulary(field):
             if eid is not None:
--- a/web/controller.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/controller.py	Fri Jul 31 14:25:30 2009 +0200
@@ -86,14 +86,16 @@
 
     def process_rql(self, rql):
         """execute rql if specified"""
+        # XXX assigning to self really necessary?
+        self.rset = None
         if rql:
             self.ensure_ro_rql(rql)
             if not isinstance(rql, unicode):
                 rql = unicode(rql, self.req.encoding)
-            pp = self.vreg.select_component('magicsearch', self.req)
-            self.rset = pp.process_query(rql, self.req)
-            return self.rset
-        return None
+            pp = self.vreg.select_object('components', 'magicsearch', self.req)
+            if pp is not None:
+                self.rset = pp.process_query(rql, self.req)
+        return self.rset
 
     def check_expected_params(self, params):
         """check that the given list of parameters are specified in the form
Binary file web/data/accessories-text-editor.png has changed
--- a/web/data/cubicweb.edition.js	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/cubicweb.edition.js	Fri Jul 31 14:25:30 2009 +0200
@@ -477,7 +477,7 @@
     d.addCallback(function (result, req) {
         handleFormValidationResponse(divid+'-form', noop, noop, result);
 	if (reload) {
-	    document.location.href = result[1];
+	    document.location.href = result[1].split('?')[0];
 	} else {
 	    var fieldview = getNode('value-' + divid);
 	    // XXX using innerHTML is very fragile and won't work if
--- a/web/data/cubicweb.flot.js	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/cubicweb.flot.js	Fri Jul 31 14:25:30 2009 +0200
@@ -1,32 +1,34 @@
 function showTooltip(x, y, contents) {
     $('<div id="tooltip">' + contents + '</div>').css( {
             position: 'absolute',
-	    display: 'none',
-	    top: y + 5,
+        display: 'none',
+        top: y + 5,
             left: x + 5,
             border: '1px solid #fdd',
             padding: '2px',
             'background-color': '#fee',
             opacity: 0.80
-		}).appendTo("body").fadeIn(200);
+        }).appendTo("body").fadeIn(200);
 }
 
 var previousPoint = null;
 function onPlotHover(event, pos, item) {
     if (item) {
         if (previousPoint != item.datapoint) {
-    	previousPoint = item.datapoint;
-    	
-    	$("#tooltip").remove();
-    	var x = item.datapoint[0].toFixed(2),
-    	    y = item.datapoint[1].toFixed(2);
-	if (item.datapoint.length == 3) {
-	    var x = new Date(item.datapoint[2]);
-	    x = x.toLocaleDateString() + ' ' + x.toLocaleTimeString();
-	}
-    	showTooltip(item.pageX, item.pageY,
-    		    item.series.label + ': (' + x + ' ; ' + y + ')');
+            previousPoint = item.datapoint;
+            $("#tooltip").remove();
+            var x = item.datapoint[0].toFixed(2),
+                y = item.datapoint[1].toFixed(2);
+            if (item.datapoint.length == 3) {
+                x = new Date(item.datapoint[2]);
+                x = x.toLocaleDateString() + ' ' + x.toLocaleTimeString();
+            } else if (item.datapoint.length == 4) {
+               x = new Date(item.datapoint[2]);
+               x = x.strftime(item.datapoint[3]);
             }
+            showTooltip(item.pageX, item.pageY,
+            item.series.label + ': (' + x + ' ; ' + y + ')');
+        }
     } else {
         $("#tooltip").remove();
         previousPoint = null;
--- a/web/data/cubicweb.htmlhelpers.js	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/cubicweb.htmlhelpers.js	Fri Jul 31 14:25:30 2009 +0200
@@ -16,78 +16,6 @@
     return '';
 }
 
-// XXX this is used exactly ONCE in web/views/massmailing.py
-function insertText(text, areaId) {
-    var textarea = jQuery('#' + areaId);
-    if (document.selection) { // IE
-        var selLength;
-        textarea.focus();
-        sel = document.selection.createRange();
-        selLength = sel.text.length;
-        sel.text = text;
-        sel.moveStart('character', selLength-text.length);
-        sel.select();
-    } else if (textarea.selectionStart || textarea.selectionStart == '0') { // mozilla
-        var startPos = textarea.selectionStart;
-        var endPos = textarea.selectionEnd;
-	// insert text so that it replaces the [startPos, endPos] part
-        textarea.value = textarea.value.substring(0,startPos) + text + textarea.value.substring(endPos,textarea.value.length);
-	// set cursor pos at the end of the inserted text
-        textarea.selectionStart = textarea.selectionEnd = startPos+text.length;
-        textarea.focus();
-    } else { // safety belt for other browsers
-        textarea.value += text;
-    }
-}
-
-/* taken from dojo toolkit */
-// XXX this looks unused
-function setCaretPos(element, start, end){
-    if(!end){ end = element.value.length; }  // NOTE: Strange - should be able to put caret at start of text?
-    // Mozilla
-    // parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
-    if(element.setSelectionRange){
-        element.focus();
-        element.setSelectionRange(start, end);
-    } else if(element.createTextRange){ // IE
-        var range = element.createTextRange();
-        with(range){
-            collapse(true);
-            moveEnd('character', end);
-            moveStart('character', start);
-            select();
-        }
-    } else { //otherwise try the event-creation hack (our own invention)
-        // do we need these?
-        element.value = element.value;
-        element.blur();
-        element.focus();
-        // figure out how far back to go
-        var dist = parseInt(element.value.length)-end;
-        var tchar = String.fromCharCode(37);
-        var tcc = tchar.charCodeAt(0);
-        for(var x = 0; x < dist; x++){
-            var te = document.createEvent("KeyEvents");
-            te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
-            element.dispatchEvent(te);
-        }
-    }
-}
-
-
-// XXX this looks unused
-function setProgressMessage(label) {
-    var body = document.getElementsByTagName('body')[0];
-    body.appendChild(DIV({id: 'progress'}, label));
-    jQuery('#progress').show();
-}
-
-// XXX this looks unused
-function resetProgressMessage() {
-    var body = document.getElementsByTagName('body')[0];
-    jQuery('#progress').hide();
-}
-
 
 /* set body's cursor to 'progress' */
 function setProgressCursor() {
@@ -151,7 +79,7 @@
 
 
 /* toggles visibility of login popup div */
-// XXX used exactly ONCE
+// XXX used exactly ONCE in basecomponents
 function popupLoginBox() {
     toggleVisibility('popupLoginBox');
     jQuery('#__login:visible').focus();
@@ -193,14 +121,6 @@
     forEach(filter(filterfunc, elements), function(cb) {cb.checked=checked;});
 }
 
-/* centers an HTML element on the screen */
-// XXX looks unused
-function centerElement(obj){
-    var vpDim = getViewportDimensions();
-    var elemDim = getElementDimensions(obj);
-    setElementPosition(obj, {'x':((vpDim.w - elemDim.w)/2),
-			     'y':((vpDim.h - elemDim.h)/2)});
-}
 
 /* this function is a hack to build a dom node from html source */
 function html2dom(source) {
@@ -223,12 +143,6 @@
 function isTextNode(domNode) { return domNode.nodeType == 3; }
 function isElementNode(domNode) { return domNode.nodeType == 1; }
 
-// XXX this looks unused
-function changeLinkText(link, newText) {
-    jQuery(link).text(newText);
-}
-
-
 function autogrow(area) {
     if (area.scrollHeight > area.clientHeight && !window.opera) {
 	if (area.rows < 20) {
@@ -236,13 +150,6 @@
 	}
     }
 }
-
-// XXX this looks unused
-function limitTextAreaSize(textarea, size) {
-    var $area = jQuery(textarea);
-    $area.val($area.val().slice(0, size));
-}
-
 //============= page loading events ==========================================//
 
 CubicWeb.rounded = [
@@ -250,8 +157,6 @@
 		    ['div.boxTitle, div.boxPrefTitle, div.sideBoxTitle, th.month', 'top 6px']
 		    ];
 
-
-
 function roundedCorners(node) {
     node = jQuery(node);
     for(var r=0; r < CubicWeb.rounded.length; r++) {
@@ -259,7 +164,8 @@
     }
 }
 
-jQuery(document).ready(function () {roundedCorners(this.body)});
+jQuery(document).ready(function () {roundedCorners(this.body);});
+
+CubicWeb.provide('corners.js');
 
 CubicWeb.provide('htmlhelpers.js');
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/cubicweb.massmailing.js	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,23 @@
+
+function insertText(text, areaId) {
+    var textarea = jQuery('#' + areaId);
+    if (document.selection) { // IE
+        var selLength;
+        textarea.focus();
+        sel = document.selection.createRange();
+        selLength = sel.text.length;
+        sel.text = text;
+        sel.moveStart('character', selLength-text.length);
+        sel.select();
+    } else if (textarea.selectionStart || textarea.selectionStart == '0') { // mozilla
+        var startPos = textarea.selectionStart;
+        var endPos = textarea.selectionEnd;
+	// insert text so that it replaces the [startPos, endPos] part
+        textarea.value = textarea.value.substring(0,startPos) + text + textarea.value.substring(endPos,textarea.value.length);
+	// set cursor pos at the end of the inserted text
+        textarea.selectionStart = textarea.selectionEnd = startPos+text.length;
+        textarea.focus();
+    } else { // safety belt for other browsers
+        textarea.value += text;
+    }
+}
--- a/web/data/cubicweb.python.js	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/cubicweb.python.js	Fri Jul 31 14:25:30 2009 +0200
@@ -157,7 +157,7 @@
 // ========== ARRAY EXTENSIONS ========== ///
 Array.prototype.contains = function(element) {
     return findValue(this, element) != -1;
-}
+};
 
 // ========== END OF ARRAY EXTENSIONS ========== ///
 
@@ -201,7 +201,7 @@
  * [0,2,4,6,8]
  */
 function list(iterable) {
-    iterator = iter(iterable);
+    var iterator = iter(iterable);
     var result = [];
     while (true) {
 	/* iterates until StopIteration occurs */
@@ -267,14 +267,6 @@
 function min() { return listMin(arguments); }
 function max() { return listMax(arguments); }
 
-// tricky multiple assign
-// function assign(lst, varnames) {
-//     var length = min(lst.length, varnames.length);
-//     for(var i=0; i<length; i++) {
-// 	window[varnames[i]] = lst[i];
-//     }
-// }
-
 /*
  * >>> d = dict(["x", "y", "z"], [0, 1, 2])
  * >>> d['y']
@@ -335,7 +327,7 @@
 function makeConstructor(userctor) {
     return function() {
 	// this is a proxy to user's __init__
-	if(userctor) {
+	if (userctor) {
 	    userctor.apply(this, arguments);
 	}
     };
@@ -369,7 +361,7 @@
 	}
     }
     var userctor = basemeths['__init__'];
-    constructor = makeConstructor(userctor);
+    var constructor = makeConstructor(userctor);
 
     // python-like interface
     constructor.__name__ = name;
--- a/web/data/cubicweb.widgets.js	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/cubicweb.widgets.js	Fri Jul 31 14:25:30 2009 +0200
@@ -184,8 +184,7 @@
 Widgets.TreeView = defclass("TreeView", null, {
     __init__: function(wdgnode) {
 	jQuery(wdgnode).treeview({toggle: toggleTree,
-				  prerendered: true
-				 });
+				  prerendered: true});
     }
 });
 
--- a/web/data/external_resources	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/data/external_resources	Fri Jul 31 14:25:30 2009 +0200
@@ -18,7 +18,7 @@
 #IE_STYLESHEETS = DATADIR/cubicweb.ie.css
 
 # Javascripts files to include in HTML headers
-#JAVASCRIPTS = DATADIR/jqyery.js, DATADIR/cubicweb.python.js, DATADIR/jquery.json.js, DATADIR/cubicweb.compat.js, DATADIR/cubicweb.htmlhelpers.js
+#JAVASCRIPTS = DATADIR/jquery.js, DATADIR/cubicweb.python.js, DATADIR/jquery.json.js, DATADIR/cubicweb.compat.js, DATADIR/cubicweb.htmlhelpers.js
 
 # path to favicon (relative to the application main script, seen as a
 # directory, hence .. when you are not using an absolute path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/jquery.tools.min.js	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,20 @@
+/*
+ * jquery.tools 1.0.2 - The missing UI library
+ * 
+ * [tools.tabs-1.0.1, tools.tooltip-1.0.2, tools.scrollable-1.0.5, tools.overlay-1.0.4, tools.expose-1.0.3]
+ * 
+ * Copyright (c) 2009 Tero Piirainen
+ * http://flowplayer.org/tools/
+ *
+ * Dual licensed under MIT and GPL 2+ licenses
+ * http://www.opensource.org/licenses
+ * 
+ * -----
+ * 
+ * Build: Fri Jun 12 12:37:07 GMT+00:00 2009
+ */
+(function(c){c.tools=c.tools||{version:{}};c.tools.version.tabs="1.0.1";c.tools.addTabEffect=function(d,e){b[d]=e};var b={"default":function(d){this.getPanes().hide().eq(d).show()},fade:function(d){this.getPanes().hide().eq(d).fadeIn(this.getConf().fadeInSpeed)},slide:function(d){this.getCurrentPane().slideUp("fast");this.getPanes().eq(d).slideDown()},horizontal:function(d){if(!c._hW){c._hW=this.getPanes().eq(0).width()}this.getCurrentPane().animate({width:0},function(){c(this).hide()});this.getPanes().eq(d).animate({width:c._hW},function(){c(this).show()})}};function a(e,f,g){var d=this;var h;function i(j,k){c(d).bind(j,function(m,l){if(k&&k.call(this,l.index)===false&&l){l.proceed=false}});return d}c.each(g,function(j,k){if(c.isFunction(k)){i(j,k)}});c.extend(this,{click:function(k){if(k===h){return d}var m=d.getCurrentPane();var l=e.eq(k);if(typeof k=="string"){l=e.filter("[href="+k+"]");k=e.index(l)}if(!l.length){if(h>=0){return d}k=g.initialIndex;l=e.eq(k)}var j={index:k,proceed:true};c(d).triggerHandler("onBeforeClick",j);if(!j.proceed){return d}l.addClass(g.current);b[g.effect].call(d,k);c(d).triggerHandler("onClick",j);e.removeClass(g.current);l.addClass(g.current);h=k;return d},getConf:function(){return g},getTabs:function(){return e},getPanes:function(){return f},getCurrentPane:function(){return f.eq(h)},getCurrentTab:function(){return e.eq(h)},getIndex:function(){return h},next:function(){return d.click(h+1)},prev:function(){return d.click(h-1)},onBeforeClick:function(j){return i("onBeforeClick",j)},onClick:function(j){return i("onClick",j)}});e.each(function(j){c(this).bind(g.event,function(k){d.click(j);if(!g.history){return k.preventDefault()}})});if(g.history){e.history(function(j,k){d.click(k||0)})}if(location.hash){d.click(location.hash)}else{d.click(g.initialIndex)}f.find("a[href^=#]").click(function(){d.click(c(this).attr("href"))})}c.fn.tabs=function(g,d){var e=this.eq(typeof conf=="number"?conf:0).data("tabs");if(e){return e}var f={tabs:"a",current:"current",onBeforeClick:null,onClick:null,effect:"default",history:false,initialIndex:0,event:"click",api:false};if(c.isFunction(d)){d={onBeforeClick:d}}c.extend(f,d);this.each(function(){var h=c(this).find(f.tabs);if(!h.length){h=c(this).children()}var i=g.jquery?g:c(g);e=new a(h,i,f);c(this).data("tabs",e)});return f.api?e:this}})(jQuery);(function(b){var c,a;b.prototype.history=function(e){var d=this;if(b.browser.msie){if(!a){a=b("<iframe />").hide().get(0);b("body").append(a);setInterval(function(){var f=a.contentWindow.document;var g=f.location.hash;if(c!==g){b.event.trigger("hash",g);c=g}},100)}d.bind("click.hash",function(g){var f=a.contentWindow.document;f.open().close();f.location.hash=b(this).attr("href")});d.eq(0).triggerHandler("click.hash")}else{setInterval(function(){var f=location.hash;if(d.filter("[href*="+f+"]").length&&f!==c){c=f;b.event.trigger("hash",f)}},100)}b(window).bind("hash",e);return this}})(jQuery);
+(function(c){c.tools=c.tools||{version:{}};c.tools.version.tooltip="1.0.2";var b={toggle:[function(){this.getTip().show()},function(){this.getTip().hide()}],fade:[function(){this.getTip().fadeIn(this.getConf().fadeInSpeed)},function(){this.getTip().fadeOut(this.getConf().fadeOutSpeed)}]};c.tools.addTipEffect=function(d,f,e){b[d]=[f,e]};c.tools.addTipEffect("slideup",function(){var d=this.getConf();var e=d.slideOffset||10;this.getTip().css({opacity:0}).animate({top:"-="+e,opacity:d.opacity},d.slideInSpeed||200).show()},function(){var d=this.getConf();var e=d.slideOffset||10;this.getTip().animate({top:"-="+e,opacity:0},d.slideOutSpeed||200,function(){c(this).hide().animate({top:"+="+(e*2)},0)})});function a(f,e){var d=this;var h=f.next();if(e.tip){if(e.tip.indexOf("#")!=-1){h=c(e.tip)}else{h=f.nextAll(e.tip).eq(0);if(!h.length){h=f.parent().nextAll(e.tip).eq(0)}}}function j(k,l){c(d).bind(k,function(n,m){if(l&&l.call(this)===false&&m){m.proceed=false}});return d}c.each(e,function(k,l){if(c.isFunction(l)){j(k,l)}});var g=f.is("input, textarea");f.bind(g?"focus":"mouseover",function(k){k.target=this;d.show(k);h.hover(function(){d.show()},function(){d.hide()})});f.bind(g?"blur":"mouseout",function(){d.hide()});h.css("opacity",e.opacity);var i=0;c.extend(d,{show:function(q){if(q){f=c(q.target)}clearTimeout(i);if(h.is(":animated")||h.is(":visible")){return d}var o={proceed:true};c(d).trigger("onBeforeShow",o);if(!o.proceed){return d}var n=f.position().top-h.outerHeight();var k=h.outerHeight()+f.outerHeight();var r=e.position[0];if(r=="center"){n+=k/2}if(r=="bottom"){n+=k}var l=f.outerWidth()+h.outerWidth();var m=f.position().left+f.outerWidth();r=e.position[1];if(r=="center"){m-=l/2}if(r=="left"){m-=l}n+=e.offset[0];m+=e.offset[1];h.css({position:"absolute",top:n,left:m});b[e.effect][0].call(d);c(d).trigger("onShow");return d},hide:function(){clearTimeout(i);i=setTimeout(function(){if(!h.is(":visible")){return d}var k={proceed:true};c(d).trigger("onBeforeHide",k);if(!k.proceed){return d}b[e.effect][1].call(d);c(d).trigger("onHide")},e.delay||1);return d},isShown:function(){return h.is(":visible, :animated")},getConf:function(){return e},getTip:function(){return h},getTrigger:function(){return f},onBeforeShow:function(k){return j("onBeforeShow",k)},onShow:function(k){return j("onShow",k)},onBeforeHide:function(k){return j("onBeforeHide",k)},onHide:function(k){return j("onHide",k)}})}c.prototype.tooltip=function(d){var e=this.eq(typeof d=="number"?d:0).data("tooltip");if(e){return e}var f={tip:null,effect:"slideup",delay:30,opacity:1,position:["top","center"],offset:[0,0],api:false};if(c.isFunction(d)){d={onBeforeShow:d}}c.extend(f,d);this.each(function(){e=new a(c(this),f);c(this).data("tooltip",e)});return f.api?e:this}})(jQuery);
+(function(b){b.tools=b.tools||{version:{}};b.tools.version.scrollable="1.0.5";var c=null;function a(p,m){var s=this;if(!c){c=s}function n(t,u){b(s).bind(t,function(w,v){if(u&&u.call(this,v.index)===false&&v){v.proceed=false}});return s}b.each(m,function(t,u){if(b.isFunction(u)){n(t,u)}});var d=!m.vertical;var f=b(m.items,p);var j=0;function l(u,t){return u.indexOf("#")!=-1?b(u).eq(0):t.siblings(u).eq(0)}var q=l(m.navi,p);var g=l(m.prev,p);var i=l(m.next,p);var h=l(m.prevPage,p);var o=l(m.nextPage,p);b.extend(s,{getIndex:function(){return j},getConf:function(){return m},getSize:function(){return s.getItems().size()},getPageAmount:function(){return Math.ceil(this.getSize()/m.size)},getPageIndex:function(){return Math.ceil(j/m.size)},getRoot:function(){return p},getItemWrap:function(){return f},getItems:function(){return f.children()},getVisibleItems:function(){return s.getItems().slice(j,j+m.size)},seekTo:function(w,u,A){if(u===undefined){u=m.speed}if(b.isFunction(u)){A=u;u=m.speed}if(w<0){w=0}if(w>s.getSize()-m.size){return s}var B=s.getItems().eq(w);if(!B.length){return s}var t={index:w,proceed:true};b(s).trigger("onBeforeSeek",t);if(!t.proceed){return s}if(d){var v=-B.position().left;f.animate({left:v},u,m.easing,A?function(){A.call(s)}:null)}else{var z=-B.position().top;f.animate({top:z},u,m.easing,A?function(){A.call(s)}:null)}if(q.length){var x=m.activeClass;var y=Math.ceil(w/m.size);y=Math.min(y,q.children().length-1);q.children().removeClass(x).eq(y).addClass(x)}if(w===0){g.add(h).addClass(m.disabledClass)}else{g.add(h).removeClass(m.disabledClass)}if(w>=s.getSize()-m.size){i.add(o).addClass(m.disabledClass)}else{i.add(o).removeClass(m.disabledClass)}c=s;j=w;b(s).trigger("onSeek",{index:w});return s},move:function(v,u,t){var w=j+v;if(m.loop&&w>(s.getSize()-m.size)){w=0}return this.seekTo(w,u,t)},next:function(u,t){return this.move(1,u,t)},prev:function(u,t){return this.move(-1,u,t)},movePage:function(v,u,t){return this.move(m.size*v,u,t)},setPage:function(x,y,v){var u=m.size;var t=u*x;var w=t+u>=this.getSize();if(w){t=this.getSize()-m.size}return this.seekTo(t,y,v)},prevPage:function(u,t){return this.setPage(this.getPageIndex()-1,u,t)},nextPage:function(u,t){return this.setPage(this.getPageIndex()+1,u,t)},begin:function(u,t){return this.seekTo(0,u,t)},end:function(u,t){return this.seekTo(this.getSize()-m.size,u,t)},reload:function(){return r()},click:function(u,x,v){var w=s.getItems().eq(u);var t=m.activeClass;if(u<0||u>=this.getSize()){return s}if(m.size==2){if(u==s.getIndex()){u--}s.getItems().removeClass(t);w.addClass(t);return this.seekTo(u,x,v)}if(!w.hasClass(t)){s.getItems().removeClass(t);w.addClass(t);var z=Math.floor(m.size/2);var y=u-z;if(y>s.getSize()-m.size){y=s.getSize()-m.size}if(y!==u){return this.seekTo(y,x,v)}}return s},onBeforeSeek:function(t){return n("onBeforeSeek",t)},onSeek:function(t){return n("onSeek",t)}});if(b.isFunction(b.fn.mousewheel)){p.bind("mousewheel.scrollable",function(u,v){var t=b.browser.opera?1:-1;s.move(v>0?t:-t,50);return false})}g.addClass(m.disabledClass).click(function(){s.prev()});i.click(function(){s.next()});o.click(function(){s.nextPage()});h.addClass(m.disabledClass).click(function(){s.prevPage()});if(m.keyboard){b(document).unbind("keydown.scrollable").bind("keydown.scrollable",function(t){var u=c;if(!u||t.altKey||t.ctrlKey){return}if(d&&(t.keyCode==37||t.keyCode==39)){u.move(t.keyCode==37?-1:1);return t.preventDefault()}if(!d&&(t.keyCode==38||t.keyCode==40)){u.move(t.keyCode==38?-1:1);return t.preventDefault()}return true})}function r(){if(q.is(":empty")||q.data("me")==s){q.empty();q.data("me",s);for(var u=0;u<s.getPageAmount();u++){var v=b("<"+m.naviItem+"/>").attr("href",u).click(function(x){var w=b(this);w.parent().children().removeClass(m.activeClass);w.addClass(m.activeClass);s.setPage(w.attr("href"));return x.preventDefault()});if(u===0){v.addClass(m.activeClass)}q.append(v)}}else{var t=q.children();t.each(function(w){var x=b(this);x.attr("href",w);if(w===0){x.addClass(m.activeClass)}x.click(function(){q.find("."+m.activeClass).removeClass(m.activeClass);x.addClass(m.activeClass);s.setPage(x.attr("href"))})})}if(m.clickable){s.getItems().each(function(x,w){var y=b(this);if(!y.data("set")){y.bind("click.scrollable",function(){s.click(x)});y.data("set",true)}})}if(m.hoverClass){s.getItems().hover(function(){b(this).addClass(m.hoverClass)},function(){b(this).removeClass(m.hoverClass)})}return s}r();var e=null;function k(){if(e){return}e=setInterval(function(){if(m.interval===0){clearInterval(e);e=0;return}s.next()},m.interval)}if(m.interval>0){p.hover(function(){clearInterval(e);e=0},function(){k()});k()}}b.fn.scrollable=function(d){var e=this.eq(typeof d=="number"?d:0).data("scrollable");if(e){return e}var f={size:5,vertical:false,clickable:true,loop:false,interval:0,speed:400,keyboard:true,activeClass:"active",disabledClass:"disabled",hoverClass:null,easing:"swing",items:".items",prev:".prev",next:".next",prevPage:".prevPage",nextPage:".nextPage",navi:".navi",naviItem:"a",api:false,onBeforeSeek:null,onSeek:null};b.extend(f,d);this.each(function(){e=new a(b(this),f);b(this).data("scrollable",e)});return f.api?e:this}})(jQuery);
+(function(b){b.tools=b.tools||{version:{}};b.tools.version.overlay="1.0.4";var c=[];function a(h,d){var r=this,q=b(window),f,n,s,i,k,m,l;var e=d.expose&&b.tools.version.expose;function p(o,t){b(r).bind(o,function(v,u){if(t&&t.call(this)===false&&u){u.proceed=false}});return r}b.each(d,function(o,t){if(b.isFunction(t)){p(o,t)}});var j=d.target||h.attr("rel");var g=j?b(j):null;if(!g){g=h}else{k=h}q.load(function(){m=g.attr("overlay");if(!m){m=g.css("backgroundImage");if(!m){throw"background-image CSS property not set for overlay element: "+j}m=m.substring(m.indexOf("(")+1,m.indexOf(")")).replace(/\"/g,"");g.css("backgroundImage","none");g.attr("overlay",m)}s=g.outerWidth({margin:true});i=g.outerHeight({margin:true});n=b('<img src="'+m+'"/>');n.css({border:0,position:"absolute",display:"none"}).width(s).attr("overlay",true);b("body").append(n);if(k){k.bind("click.overlay",function(o){r.load(o.pageY-q.scrollTop(),o.pageX-q.scrollLeft());return o.preventDefault()})}d.close=d.close||".close";if(!g.find(d.close).length){g.prepend('<div class="close"></div>')}f=g.find(d.close);f.bind("click.overlay",function(){r.close()});if(d.preload){setTimeout(function(){var o=new Image();o.src=m},2000)}});b.extend(r,{load:function(w,v){if(!n){q.load(function(){r.load(w,v)});return r}if(r.isOpened()){return r}if(d.oneInstance){b.each(c,function(){this.close()})}var u={proceed:true};b(r).trigger("onBeforeLoad",u);if(!u.proceed){return r}if(e){n.expose(d.expose);l=n.expose().load()}w=w||d.start.top;v=v||d.start.left;var o=d.finish.top;var t=d.finish.left;if(o=="center"){o=Math.max((q.height()-i)/2,0)}if(t=="center"){t=Math.max((q.width()-s)/2,0)}if(!d.start.absolute){w+=q.scrollTop();v+=q.scrollLeft()}if(!d.finish.absolute){o+=q.scrollTop();t+=q.scrollLeft()}n.css({top:w,left:v,width:d.start.width,zIndex:d.zIndex}).show();n.animate({top:o,left:t,width:s},d.speed,function(){g.css({position:"absolute",top:o,left:t});var x=n.css("zIndex");f.add(g).css("zIndex",++x);g.fadeIn(d.fadeInSpeed,function(){b(r).trigger("onLoad")})});return r},close:function(){if(!r.isOpened()){return r}var u={proceed:true};b(r).trigger("onBeforeClose",u);if(!u.proceed){return r}if(l){l.close()}if(n.is(":visible")){g.hide();var t=d.start.top;var o=d.start.left;if(k){u=k.offset();t=u.top+k.height()/2;o=u.left+k.width()/2}n.animate({top:t,left:o,width:0},d.closeSpeed,function(){b(r).trigger("onClose",u)})}return r},getBackgroundImage:function(){return n},getContent:function(){return g},getTrigger:function(){return k},isOpened:function(){return g.is(":visible")},getConf:function(){return d},onBeforeLoad:function(o){return p("onBeforeLoad",o)},onLoad:function(o){return p("onLoad",o)},onBeforeClose:function(o){return p("onBeforeClose",o)},onClose:function(o){return p("onClose",o)}});b(document).keydown(function(o){if(o.keyCode==27){r.close()}});if(d.closeOnClick){b(document).bind("click.overlay",function(o){if(!g.is(":visible, :animated")){return}var t=b(o.target);if(t.attr("overlay")){return}if(t.parents("[overlay]").length){return}r.close()})}}b.fn.overlay=function(e){var f=this.eq(typeof e=="number"?e:0).data("overlay");if(f){return f}var d=b(window);var g={start:{top:Math.round(d.height()/2),left:Math.round(d.width()/2),width:0,absolute:false},finish:{top:80,left:"center",absolute:false},speed:"normal",fadeInSpeed:"fast",closeSpeed:"fast",close:null,oneInstance:true,closeOnClick:true,preload:true,zIndex:9999,api:false,expose:null,target:null};if(b.isFunction(e)){e={onBeforeLoad:e}}b.extend(true,g,e);this.each(function(){f=new a(b(this),g);c.push(f);b(this).data("overlay",f)});return g.api?f:this}})(jQuery);
+(function(b){b.tools=b.tools||{version:{}};b.tools.version.expose="1.0.3";function a(){var e=b(window).width();if(b.browser.mozilla){return e}var d;if(window.innerHeight&&window.scrollMaxY){d=window.innerWidth+window.scrollMaxX}else{if(document.body.scrollHeight>document.body.offsetHeight){d=document.body.scrollWidth}else{d=document.body.offsetWidth}}return d<e?d+20:e}function c(g,h){var e=this,d=null,f=false,i=0;function j(k,l){b(e).bind(k,function(n,m){if(l&&l.call(this)===false&&m){m.proceed=false}});return e}b.each(h,function(k,l){if(b.isFunction(l)){j(k,l)}});b(window).bind("resize.expose",function(){if(d){d.css({width:a(),height:b(document).height()})}});b.extend(this,{getMask:function(){return d},getExposed:function(){return g},getConf:function(){return h},isLoaded:function(){return f},load:function(){if(f){return e}i=g.eq(0).css("zIndex");if(h.maskId){d=b("#"+h.maskId)}if(!d||!d.length){d=b("<div/>").css({position:"absolute",top:0,left:0,width:a(),height:b(document).height(),display:"none",opacity:0,zIndex:h.zIndex});if(h.maskId){d.attr("id",h.maskId)}b("body").append(d);var k=d.css("backgroundColor");if(!k||k=="transparent"||k=="rgba(0, 0, 0, 0)"){d.css("backgroundColor",h.color)}if(h.closeOnEsc){b(document).bind("keydown.unexpose",function(n){if(n.keyCode==27){e.close()}})}if(h.closeOnClick){d.bind("click.unexpose",function(){e.close()})}}var m={proceed:true};b(e).trigger("onBeforeLoad",m);if(!m.proceed){return e}b.each(g,function(){var n=b(this);if(!/relative|absolute|fixed/i.test(n.css("position"))){n.css("position","relative")}});g.css({zIndex:h.zIndex+1});var l=d.height();if(!this.isLoaded()){d.css({opacity:0,display:"block"}).fadeTo(h.loadSpeed,h.opacity,function(){if(d.height()!=l){d.css("height",l)}b(e).trigger("onLoad")})}f=true;return e},close:function(){if(!f){return e}var k={proceed:true};b(e).trigger("onBeforeClose",k);if(k.proceed===false){return e}d.fadeOut(h.closeSpeed,function(){b(e).trigger("onClose");g.css({zIndex:b.browser.msie?i:null})});f=false;return e},onBeforeLoad:function(k){return j("onBeforeLoad",k)},onLoad:function(k){return j("onLoad",k)},onBeforeClose:function(k){return j("onBeforeClose",k)},onClose:function(k){return j("onClose",k)}})}b.fn.expose=function(d){var e=this.eq(typeof d=="number"?d:0).data("expose");if(e){return e}var f={maskId:null,loadSpeed:"slow",closeSpeed:"fast",closeOnClick:true,closeOnEsc:true,zIndex:9998,opacity:0.8,color:"#456",api:false};if(typeof d=="string"){d={color:d}}b.extend(f,d);this.each(function(){e=new c(b(this),f);b(this).data("expose",e)});return f.api?e:this}})(jQuery);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/tabs-no-images.css	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,59 @@
+
+/* root element for tabs  */
+ul.css-tabs {
+	margin:0 !important;
+	padding:0;
+	height:20px;
+	border-bottom:1px solid #666;
+}
+
+/* single tab */
+ul.css-tabs li {
+	float:left;
+	padding:0;
+	margin:0;
+	list-style-type:none;
+}
+
+/* link inside the tab. uses a background image */
+ul.css-tabs a {
+	float:left;
+	font-size:12px;
+	display:block;
+	padding:2px 20px;
+	text-decoration:none;
+	border:1px solid #666;
+	border-bottom:0px;
+	height:14px;
+	background-color:#efefef;
+	color:#777;
+	margin-right:2px;
+	position:relative;
+	top:1px;
+}
+
+ul.css-tabs a:hover {
+	background-color:#CFCEB7;
+	color:#333;
+}
+
+/* selected tab */
+ul.css-tabs a.current {
+	background-color: white;
+	border-bottom:2px solid white;
+	color:#000;
+}
+
+
+/* tab pane */
+div.css-panes div {
+	display:none;
+	border:1px solid #666;
+	border-width:0 1px 1px 1px;
+	min-height:150px;
+	padding:15px 20px;
+	/* background-color:#ddd; */
+}
+
+
+
--- a/web/data/ui.tabs.css	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/* Caution! Ensure accessibility in print and other media types... */
-@media projection, screen { /* Use class for showing/hiding tab content, so that visibility can be better controlled in different media types... */
-    .ui-tabs-hide {
-        display: none;
-    }
-}
-
-/* Hide useless elements in print layouts... */
-@media print {
-    .ui-tabs-nav {
-        display: none;
-    }
-}
-
-/* Skin */
-.ui-tabs-nav, .ui-tabs-panel {
-    font-family: "Trebuchet MS", Trebuchet, Verdana, Helvetica, Arial, sans-serif;
-    font-size: 12px;
-
-}
-.ui-tabs-nav {
-    list-style: none;
-    margin: 0px;
-    padding: 0px 0px 0px 4px; 
-
-}
-.ui-tabs-nav:after { /* clearing without presentational markup, IE gets extra treatment */
-    display: block;
-    clear: both;
-    content: " ";
-}
-.ui-tabs-nav li {
-    float: left;
-    margin: 0 0 0 1px;
-    min-width: 84px; /* be nice to Opera */
-    list-style: none;
-    background: none;
-    padding: 0px 0px 1px 1px;
-}
-.ui-tabs-nav a, .ui-tabs-nav a span {
-    display: block;
-    padding: 0 10px;
-    background: url(tab.png) no-repeat;
-}
-.ui-tabs-nav a {
-    margin: 1px 0 0; /* position: relative makes opacity fail for disabled tab in IE */
-    padding-left: 0;
-    color: #27537a;
-    font-weight: bold;
-    line-height: 1.2;
-    text-align: center;
-    text-decoration: none;
-    white-space: nowrap; /* required in IE 6 */    
-    outline: 0; /* prevent dotted border in Firefox */
-}
-.ui-tabs-nav .ui-tabs-selected a {
-    position: relative;
-    top: 1px;
-    z-index: 2;
-    margin-top: 0;
-    color: #000;
-}
-.ui-tabs-nav a span {
-    width: 64px; /* IE 6 treats width as min-width */
-    min-width: 64px;
-    height: 18px; /* IE 6 treats height as min-height */
-    min-height: 18px;
-    padding-top: 6px;
-    padding-right: 0;
-}
-*>.ui-tabs-nav a span { /* hide from IE 6 */
-    width: auto;
-    height: auto;
-}
-.ui-tabs-nav .ui-tabs-selected a span {
-    padding-bottom: 1px;
-}
-.ui-tabs-nav .ui-tabs-selected a, .ui-tabs-nav a:hover, .ui-tabs-nav a:focus, .ui-tabs-nav a:active {
-    background-position: 100% -150px;
-}
-.ui-tabs-nav a, .ui-tabs-nav .ui-tabs-disabled a:hover, .ui-tabs-nav .ui-tabs-disabled a:focus, .ui-tabs-nav .ui-tabs-disabled a:active {
-    background-position: 100% -100px;
-}
-.ui-tabs-nav .ui-tabs-selected a span, .ui-tabs-nav a:hover span, .ui-tabs-nav a:focus span, .ui-tabs-nav a:active span {
-    background-position: 0 -50px;
-}
-.ui-tabs-nav a span, .ui-tabs-nav .ui-tabs-disabled a:hover span, .ui-tabs-nav .ui-tabs-disabled a:focus span, .ui-tabs-nav .ui-tabs-disabled a:active span {
-    background-position: 0 0;
-}
-.ui-tabs-nav .ui-tabs-selected a:link, .ui-tabs-nav .ui-tabs-selected a:visited, .ui-tabs-nav .ui-tabs-disabled a:link, .ui-tabs-nav .ui-tabs-disabled a:visited { /* @ Opera, use pseudo classes otherwise it confuses cursor... */
-    cursor: text;
-}
-.ui-tabs-nav a:hover, .ui-tabs-nav a:focus, .ui-tabs-nav a:active,
-.ui-tabs-nav .ui-tabs-unselect a:hover, .ui-tabs-nav .ui-tabs-unselect a:focus, .ui-tabs-nav .ui-tabs-unselect a:active { /* @ Opera, we need to be explicit again here now... */
-    cursor: pointer;
-}
-.ui-tabs-disabled {
-    opacity: .4;
-    filter: alpha(opacity=40);
-}
-.ui-tabs-panel {
-    border-top: 1px solid #97a5b0;
-    padding: 1em 8px;
-    margin-top:-1px;  /* Logilab style */
-    background: #fff; /* declare background color for container to avoid distorted fonts in IE while fading */
-}
-.ui-tabs-loading em {
-    padding: 0 0 0 20px;
-    background: url(loading.gif) no-repeat 0 50%;
-}
-
-/* Additional IE specific bug fixes... */
-* html .ui-tabs-nav { /* auto clear, @ IE 6 & IE 7 Quirks Mode */
-    display: inline-block;
-}
-*:first-child+html .ui-tabs-nav  { /* @ IE 7 Standards Mode - do not group selectors, otherwise IE 6 will ignore complete rule (because of the unknown + combinator)... */
-    display: inline-block;
-}
-
-/* ========= Lobilab styles =========== */
-
-/* added by katia */
-* html .ui-tabs-panel{  
-    width:100%;
-}
\ No newline at end of file
--- a/web/data/ui.tabs.js	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,587 +0,0 @@
-/*
- * jQuery UI Tabs @VERSION
- *
- * Copyright (c) 2007, 2008 Klaus Hartl (stilbuero.de)
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- * http://docs.jquery.com/UI/Tabs
- *
- * Depends:
- *	ui.core.js
- */
-(function($) {
-
-$.widget("ui.tabs", {
-	_init: function() {
-		this.options.event += '.tabs'; // namespace event
-		
-		// create tabs
-		this._tabify(true);
-	},
-	_setData: function(key, value) {
-		if ((/^selected/).test(key))
-			this.select(value);
-		else {
-			this.options[key] = value;
-			this._tabify();
-		}
-	},
-	length: function() {
-		return this.$tabs.length;
-	},
-	_tabId: function(a) {
-		return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
-			|| this.options.idPrefix + $.data(a);
-	},
-	ui: function(tab, panel) {
-		return {
-			options: this.options,
-			tab: tab,
-			panel: panel,
-			index: this.$tabs.index(tab)
-		};
-	},
-	_tabify: function(init) {
-
-		this.$lis = $('li:has(a[href])', this.element);
-		this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
-		this.$panels = $([]);
-
-		var self = this, o = this.options;
-
-		this.$tabs.each(function(i, a) {
-			// inline tab
-			if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
-				self.$panels = self.$panels.add(a.hash);
-			// remote tab
-			else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
-				$.data(a, 'href.tabs', a.href); // required for restore on destroy
-				$.data(a, 'load.tabs', a.href); // mutable
-				var id = self._tabId(a);
-				a.href = '#' + id;
-				var $panel = $('#' + id);
-				if (!$panel.length) {
-					$panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
-						.insertAfter( self.$panels[i - 1] || self.element );
-					$panel.data('destroy.tabs', true);
-				}
-				self.$panels = self.$panels.add( $panel );
-			}
-			// invalid tab href
-			else
-				o.disabled.push(i + 1);
-		});
-
-		// initialization from scratch
-		if (init) {
-
-			// attach necessary classes for styling if not present
-			this.element.addClass(o.navClass);
-			this.$panels.each(function() {
-				var $this = $(this);
-				$this.addClass(o.panelClass);
-			});
-
-			// Selected tab
-			// use "selected" option or try to retrieve:
-			// 1. from fragment identifier in url
-			// 2. from cookie
-			// 3. from selected class attribute on <li>
-			if (o.selected === undefined) {
-				if (location.hash) {
-					this.$tabs.each(function(i, a) {
-						if (a.hash == location.hash) {
-							o.selected = i;
-							// prevent page scroll to fragment
-							if ($.browser.msie || $.browser.opera) { // && !o.remote
-								var $toShow = $(location.hash), toShowId = $toShow.attr('id');
-								$toShow.attr('id', '');
-								setTimeout(function() {
-									$toShow.attr('id', toShowId); // restore id
-								}, 500);
-							}
-							scrollTo(0, 0);
-							return false; // break
-						}
-					});
-				}
-				else if (o.cookie) {
-					var index = parseInt($.cookie('ui-tabs-' + $.data(self.element[0])), 10);
-					if (index && self.$tabs[index])
-						o.selected = index;
-				}
-				else if (self.$lis.filter('.' + o.selectedClass).length)
-					o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
-			}
-			o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default
-
-			// Take disabling tabs via class attribute from HTML
-			// into account and update option properly.
-			// A selected tab cannot become disabled.
-			o.disabled = $.unique(o.disabled.concat(
-				$.map(this.$lis.filter('.' + o.disabledClass),
-					function(n, i) { return self.$lis.index(n); } )
-			)).sort();
-			if ($.inArray(o.selected, o.disabled) != -1)
-				o.disabled.splice($.inArray(o.selected, o.disabled), 1);
-			
-			// highlight selected tab
-			this.$panels.addClass(o.hideClass);
-			this.$lis.removeClass(o.selectedClass);
-			if (o.selected !== null) {
-				this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
-				this.$lis.eq(o.selected).addClass(o.selectedClass);
-				
-				// seems to be expected behavior that the show callback is fired
-				var onShow = function() {
-					self._trigger('show', null,
-						self.ui(self.$tabs[o.selected], self.$panels[o.selected]));
-				};
-
-				// load if remote tab
-				if ($.data(this.$tabs[o.selected], 'load.tabs'))
-					this.load(o.selected, onShow);
-				// just trigger show event
-				else
-					onShow();
-			}
-			
-			// clean up to avoid memory leaks in certain versions of IE 6
-			$(window).bind('unload', function() {
-				self.$tabs.unbind('.tabs');
-				self.$lis = self.$tabs = self.$panels = null;
-			});
-
-		}
-		// update selected after add/remove
-		else
-			o.selected = this.$lis.index( this.$lis.filter('.' + o.selectedClass)[0] );
-
-		// set or update cookie after init and add/remove respectively
-		if (o.cookie)
-			$.cookie('ui-tabs-' + $.data(self.element[0]), o.selected, o.cookie);
-		
-		// disable tabs
-		for (var i = 0, li; li = this.$lis[i]; i++)
-			$(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);
-
-		// reset cache if switching from cached to not cached
-		if (o.cache === false)
-			this.$tabs.removeData('cache.tabs');
-		
-		// set up animations
-		var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
-		if (o.fx && o.fx.constructor == Array)
-			hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
-		else
-			hideFx = showFx = o.fx || baseFx;
-
-		// reset some styles to maintain print style sheets etc.
-		var resetCSS = { display: '', overflow: '', height: '' };
-		if (!$.browser.msie) // not in IE to prevent ClearType font issue
-			resetCSS.opacity = '';
-
-		// Hide a tab, animation prevents browser scrolling to fragment,
-		// $show is optional.
-		function hideTab(clicked, $hide, $show) {
-			$hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
-				$hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
-				if ($.browser.msie && hideFx.opacity)
-					$hide[0].style.filter = '';
-				if ($show)
-					showTab(clicked, $show, $hide);
-			});
-		}
-
-		// Show a tab, animation prevents browser scrolling to fragment,
-		// $hide is optional.
-		function showTab(clicked, $show, $hide) {
-			if (showFx === baseFx)
-				$show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
-			$show.animate(showFx, showFx.duration || baseDuration, function() {
-				$show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
-				if ($.browser.msie && showFx.opacity)
-					$show[0].style.filter = '';
-
-				// callback
-				self._trigger('show', null, self.ui(clicked, $show[0]));
-			});
-		}
-
-		// switch a tab
-		function switchTab(clicked, $li, $hide, $show) {
-			/*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
-				$.ajaxHistory.update(clicked.hash);
-			}*/
-			$li.addClass(o.selectedClass)
-				.siblings().removeClass(o.selectedClass);
-			hideTab(clicked, $hide, $show);
-		}
-
-		// attach tab event handler, unbind to avoid duplicates from former tabifying...
-		this.$tabs.unbind('.tabs').bind(o.event, function() {
-
-			//var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
-			var $li = $(this).parents('li:eq(0)'),
-				$hide = self.$panels.filter(':visible'),
-				$show = $(this.hash);
-
-			// If tab is already selected and not unselectable or tab disabled or 
-			// or is already loading or click callback returns false stop here.
-			// Check if click handler returns false last so that it is not executed
-			// for a disabled or loading tab!
-			if (($li.hasClass(o.selectedClass) && !o.unselect)
-				|| $li.hasClass(o.disabledClass) 
-				|| $(this).hasClass(o.loadingClass)
-				|| self._trigger('select', null, self.ui(this, $show[0])) === false
-				) {
-				this.blur();
-				return false;
-			}
-
-			self.options.selected = self.$tabs.index(this);
-
-			// if tab may be closed
-			if (o.unselect) {
-				if ($li.hasClass(o.selectedClass)) {
-					self.options.selected = null;
-					$li.removeClass(o.selectedClass);
-					self.$panels.stop();
-					hideTab(this, $hide);
-					this.blur();
-					return false;
-				} else if (!$hide.length) {
-					self.$panels.stop();
-					var a = this;
-					self.load(self.$tabs.index(this), function() {
-						$li.addClass(o.selectedClass).addClass(o.unselectClass);
-						showTab(a, $show);
-					});
-					this.blur();
-					return false;
-				}
-			}
-
-			if (o.cookie)
-				$.cookie('ui-tabs-' + $.data(self.element[0]), self.options.selected, o.cookie);
-
-			// stop possibly running animations
-			self.$panels.stop();
-
-			// show new tab
-			if ($show.length) {
-
-				// prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
-				/*if ($.browser.msie && o.bookmarkable) {
-					var showId = this.hash.replace('#', '');
-					$show.attr('id', '');
-					setTimeout(function() {
-						$show.attr('id', showId); // restore id
-					}, 0);
-				}*/
-
-				var a = this;
-				self.load(self.$tabs.index(this), $hide.length ? 
-					function() {
-						switchTab(a, $li, $hide, $show);
-					} :
-					function() {
-						$li.addClass(o.selectedClass);
-						showTab(a, $show);
-					}
-				);
-
-				// Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
-				/*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
-				var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
-				setTimeout(function() {
-					scrollTo(scrollX, scrollY);
-				}, 0);*/
-
-			} else
-				throw 'jQuery UI Tabs: Mismatching fragment identifier.';
-
-			// Prevent IE from keeping other link focussed when using the back button
-			// and remove dotted border from clicked link. This is controlled in modern
-			// browsers via CSS, also blur removes focus from address bar in Firefox
-			// which can become a usability and annoying problem with tabsRotate.
-			if ($.browser.msie)
-				this.blur();
-
-			//return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
-			return false;
-
-		});
-
-		// disable click if event is configured to something else
-		if (!(/^click/).test(o.event))
-			this.$tabs.bind('click.tabs', function() { return false; });
-
-	},
-	add: function(url, label, index) {
-		if (index == undefined) 
-			index = this.$tabs.length; // append by default
-
-		var o = this.options;
-		var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
-		$li.data('destroy.tabs', true);
-
-		var id = url.indexOf('#') == 0 ? url.replace('#', '') : this._tabId( $('a:first-child', $li)[0] );
-
-		// try to find an existing element before creating a new one
-		var $panel = $('#' + id);
-		if (!$panel.length) {
-			$panel = $(o.panelTemplate).attr('id', id)
-				.addClass(o.hideClass)
-				.data('destroy.tabs', true);
-		}
-		$panel.addClass(o.panelClass);
-		if (index >= this.$lis.length) {
-			$li.appendTo(this.element);
-			$panel.appendTo(this.element[0].parentNode);
-		} else {
-			$li.insertBefore(this.$lis[index]);
-			$panel.insertBefore(this.$panels[index]);
-		}
-		
-		o.disabled = $.map(o.disabled,
-			function(n, i) { return n >= index ? ++n : n });
-			
-		this._tabify();
-
-		if (this.$tabs.length == 1) {
-			$li.addClass(o.selectedClass);
-			$panel.removeClass(o.hideClass);
-			var href = $.data(this.$tabs[0], 'load.tabs');
-			if (href)
-				this.load(index, href);
-		}
-
-		// callback
-		this._trigger('add', null, this.ui(this.$tabs[index], this.$panels[index]));
-	},
-	remove: function(index) {
-		var o = this.options, $li = this.$lis.eq(index).remove(),
-			$panel = this.$panels.eq(index).remove();
-
-		// If selected tab was removed focus tab to the right or
-		// in case the last tab was removed the tab to the left.
-		if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
-			this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));
-
-		o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
-			function(n, i) { return n >= index ? --n : n });
-
-		this._tabify();
-
-		// callback
-		this._trigger('remove', null, this.ui($li.find('a')[0], $panel[0]));
-	},
-	enable: function(index) {
-		var o = this.options;
-		if ($.inArray(index, o.disabled) == -1)
-			return;
-			
-		var $li = this.$lis.eq(index).removeClass(o.disabledClass);
-		if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
-			$li.css('display', 'inline-block');
-			setTimeout(function() {
-				$li.css('display', 'block');
-			}, 0);
-		}
-
-		o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
-
-		// callback
-		this._trigger('enable', null, this.ui(this.$tabs[index], this.$panels[index]));
-	},
-	disable: function(index) {
-		var self = this, o = this.options;
-		if (index != o.selected) { // cannot disable already selected tab
-			this.$lis.eq(index).addClass(o.disabledClass);
-
-			o.disabled.push(index);
-			o.disabled.sort();
-
-			// callback
-			this._trigger('disable', null, this.ui(this.$tabs[index], this.$panels[index]));
-		}
-	},
-	select: function(index) {
-		if (typeof index == 'string')
-			index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
-		this.$tabs.eq(index).trigger(this.options.event);
-	},
-	load: function(index, callback) { // callback is for internal usage only
-		
-		var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
-				bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');
-
-		callback = callback || function() {};
-		
-		// no remote or from cache - just finish with callback
-		if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
-			callback();
-			return;
-		}
-
-		// load remote from here on
-		
-		var inner = function(parent) {
-			var $parent = $(parent), $inner = $parent.find('*:last');
-			return $inner.length && $inner.is(':not(img)') && $inner || $parent;
-		};
-		var cleanup = function() {
-			self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
-						.each(function() {
-							if (o.spinner)
-								inner(this).parent().html(inner(this).data('label.tabs'));
-						});
-			self.xhr = null;
-		};
-		
-		if (o.spinner) {
-			var label = inner(a).html();
-			inner(a).wrapInner('<em></em>')
-				.find('em').data('label.tabs', label).html(o.spinner);
-		}
-
-		var ajaxOptions = $.extend({}, o.ajaxOptions, {
-			url: url,
-			success: function(r, s) {
-				$(a.hash).html(r);
-				cleanup();
-				
-				if (o.cache)
-					$.data(a, 'cache.tabs', true); // if loaded once do not load them again
-
-				// callbacks
-				self._trigger('load', null, self.ui(self.$tabs[index], self.$panels[index]));
-				o.ajaxOptions.success && o.ajaxOptions.success(r, s);
-				
-				// This callback is required because the switch has to take
-				// place after loading has completed. Call last in order to 
-				// fire load before show callback...
-				callback();
-			}
-		});
-		if (this.xhr) {
-			// terminate pending requests from other tabs and restore tab label
-			this.xhr.abort();
-			cleanup();
-		}
-		$a.addClass(o.loadingClass);
-		setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
-			self.xhr = $.ajax(ajaxOptions);
-		}, 0);
-
-	},
-	url: function(index, url) {
-		this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
-	},
-	destroy: function() {
-		var o = this.options;
-		this.element.unbind('.tabs')
-			.removeClass(o.navClass).removeData('tabs');
-		this.$tabs.each(function() {
-			var href = $.data(this, 'href.tabs');
-			if (href)
-				this.href = href;
-			var $this = $(this).unbind('.tabs');
-			$.each(['href', 'load', 'cache'], function(i, prefix) {
-				$this.removeData(prefix + '.tabs');
-			});
-		});
-		this.$lis.add(this.$panels).each(function() {
-			if ($.data(this, 'destroy.tabs'))
-				$(this).remove();
-			else
-				$(this).removeClass([o.selectedClass, o.unselectClass,
-					o.disabledClass, o.panelClass, o.hideClass].join(' '));
-		});
-	}
-});
-
-$.ui.tabs.defaults = {
-	// basic setup
-	unselect: false,
-	event: 'click',
-	disabled: [],
-	cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
-	// TODO history: false,
-
-	// Ajax
-	spinner: 'Loading&#8230;',
-	cache: false,
-	idPrefix: 'ui-tabs-',
-	ajaxOptions: {},
-
-	// animations
-	fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
-
-	// templates
-	tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
-	panelTemplate: '<div></div>',
-
-	// CSS classes
-	navClass: 'ui-tabs-nav',
-	selectedClass: 'ui-tabs-selected',
-	unselectClass: 'ui-tabs-unselect',
-	disabledClass: 'ui-tabs-disabled',
-	panelClass: 'ui-tabs-panel',
-	hideClass: 'ui-tabs-hide',
-	loadingClass: 'ui-tabs-loading'
-};
-
-$.ui.tabs.getter = "length";
-
-/*
- * Tabs Extensions
- */
-
-/*
- * Rotate
- */
-$.extend($.ui.tabs.prototype, {
-	rotation: null,
-	rotate: function(ms, continuing) {
-		
-		continuing = continuing || false;
-		
-		var self = this, t = this.options.selected;
-		
-		function start() {
-			self.rotation = setInterval(function() {
-				t = ++t < self.$tabs.length ? t : 0;
-				self.select(t);
-			}, ms); 
-		}
-		
-		function stop(e) {
-			if (!e || e.clientX) { // only in case of a true click
-				clearInterval(self.rotation);
-			}
-		}
-		
-		// start interval
-		if (ms) {
-			start();
-			if (!continuing)
-				this.$tabs.bind(this.options.event, stop);
-			else
-				this.$tabs.bind(this.options.event, function() {
-					stop();
-					t = self.options.selected;
-					start();
-				});
-		}
-		// stop interval
-		else {
-			stop();
-			this.$tabs.unbind(this.options.event, stop);
-		}
-	}
-});
-
-})(jQuery);
--- a/web/formfields.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/formfields.py	Fri Jul 31 14:25:30 2009 +0200
@@ -11,9 +11,9 @@
 from datetime import datetime
 
 from logilab.mtconverter import xml_escape
-from yams.constraints import SizeConstraint, StaticVocabularyConstraint
+from yams.constraints import (SizeConstraint, StaticVocabularyConstraint,
+                              FormatConstraint)
 
-from cubicweb.schema import FormatConstraint
 from cubicweb.utils import ustrftime, compute_cardinality
 from cubicweb.common import tags, uilib
 from cubicweb.web import INTERNAL_FIELD_VALUE
@@ -63,6 +63,8 @@
     :role:
        when the field is linked to an entity attribute or relation, tells the
        role of the entity in the relation (eg 'subject' or 'object')
+    :fieldset:
+       optional fieldset to which this field belongs to
 
     """
     # default widget associated to this class of fields. May be overriden per
@@ -76,7 +78,7 @@
     def __init__(self, name=None, id=None, label=None, help=None,
                  widget=None, required=False, initial=None,
                  choices=None, sort=True, internationalizable=False,
-                 eidparam=False, role='subject'):
+                 eidparam=False, role='subject', fieldset=None):
         self.name = name
         self.id = id or name
         self.label = label or name
@@ -88,6 +90,7 @@
         self.internationalizable = internationalizable
         self.eidparam = eidparam
         self.role = role
+        self.fieldset = fieldset
         self.init_widget(widget)
         # ordering number for this field instance
         self.creation_rank = Field.__creation_rank
@@ -158,7 +161,13 @@
         """render this field, which is part of form, using the given form
         renderer
         """
-        return self.get_widget(form).render(form, self)
+        widget = self.get_widget(form)
+        try:
+            return widget.render(form, self, renderer)
+        except TypeError:
+            warn('widget.render now take the renderer as third argument, please update %s implementation'
+                 % widget.__class__.__name__, DeprecationWarning)
+            return widget.render(form, self)
 
     def vocabulary(self, form):
         """return vocabulary for this field. This method will be called by
@@ -287,7 +296,7 @@
             result = format_field.render(form, renderer)
         else:
             result = u''
-        return result + self.get_widget(form).render(form, self)
+        return result + self.get_widget(form).render(form, self, renderer)
 
 
 class FileField(StringField):
@@ -307,7 +316,7 @@
             yield self.encoding_field
 
     def render(self, form, renderer):
-        wdgs = [self.get_widget(form).render(form, self)]
+        wdgs = [self.get_widget(form).render(form, self, renderer)]
         if self.format_field or self.encoding_field:
             divid = '%s-advanced' % form.context[self]['name']
             wdgs.append(u'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>' %
@@ -361,7 +370,7 @@
                             'You can either submit a new file using the browse button above'
                             ', or edit file content online with the widget below.')
                     wdgs.append(u'<p><b>%s</b></p>' % msg)
-                    wdgs.append(TextArea(setdomid=False).render(form, self))
+                    wdgs.append(TextArea(setdomid=False).render(form, self, renderer))
                     # XXX restore form context?
         return '\n'.join(wdgs)
 
@@ -464,6 +473,18 @@
         return value
 
 
+class CompoundField(Field):
+    def __init__(self, fields, *args, **kwargs):
+        super(CompoundField, self).__init__(*args, **kwargs)
+        self.fields = fields
+
+    def subfields(self, form):
+        return self.fields
+
+    def actual_fields(self, form):
+        return [self] + list(self.fields)
+
+
 def guess_field(eschema, rschema, role='subject', skip_meta_attr=True, **kwargs):
     """return the most adapated widget to edit the relation
     'subjschema rschema objschema' according to information found in the schema
--- a/web/formwidgets.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/formwidgets.py	Fri Jul 31 14:25:30 2009 +0200
@@ -10,7 +10,7 @@
 from datetime import date
 from warnings import warn
 
-from cubicweb.common import tags
+from cubicweb.common import tags, uilib
 from cubicweb.web import stdmsgs, INTERNAL_FIELD_VALUE
 
 
@@ -43,7 +43,7 @@
         if self.needs_css:
             form.req.add_css(self.needs_css)
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         """render the widget for the given `field` of `form`.
         To override in concrete class
         """
@@ -68,7 +68,7 @@
     """abstract widget class for <input> tag based widgets"""
     type = None
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         """render the widget for the given `field` of `form`.
 
         Generate one <input> tag for each field's value
@@ -96,7 +96,7 @@
     """
     type = 'password'
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         self.add_media(form)
         name, values, attrs = self._render_attrs(form, field)
         assert len(values) == 1
@@ -105,9 +105,11 @@
             confirmname = '%s-confirm:%s' % tuple(name.rsplit(':', 1))
         except TypeError:
             confirmname = '%s-confirm' % name
-        inputs = [tags.input(name=name, value=values[0], type=self.type, id=id, **attrs),
+        inputs = [tags.input(name=name, value=values[0], type=self.type, id=id,
+                             **attrs),
                   '<br/>',
-                  tags.input(name=confirmname, value=values[0], type=self.type, **attrs),
+                  tags.input(name=confirmname, value=values[0], type=self.type,
+                             **attrs),
                   '&nbsp;', tags.span(form.req._('confirm password'),
                                       **{'class': 'emphasis'})]
         return u'\n'.join(inputs)
@@ -147,7 +149,7 @@
 class TextArea(FieldWidget):
     """<textarea>"""
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         name, values, attrs = self._render_attrs(form, field)
         attrs.setdefault('onkeyup', 'autogrow(this)')
         if not values:
@@ -171,9 +173,9 @@
         super(FCKEditor, self).__init__(*args, **kwargs)
         self.attrs['cubicweb:type'] = 'wysiwyg'
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         form.req.fckeditor_config()
-        return super(FCKEditor, self).render(form, field)
+        return super(FCKEditor, self).render(form, field, renderer)
 
 
 class Select(FieldWidget):
@@ -184,23 +186,30 @@
         super(Select, self).__init__(attrs)
         self._multiple = multiple
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         name, curvalues, attrs = self._render_attrs(form, field)
         if not 'size' in attrs:
             attrs['size'] = self._multiple and '5' or '1'
         options = []
         optgroup_opened = False
-        for label, value in field.vocabulary(form):
+        for option in field.vocabulary(form):
+            try:
+                label, value, oattrs = option
+            except ValueError:
+                label, value = option
+                oattrs = {}
             if value is None:
                 # handle separator
                 if optgroup_opened:
                     options.append(u'</optgroup>')
-                options.append(u'<optgroup label="%s">' % (label or ''))
+                oattrs.setdefault('label', label or '')
+                options.append(u'<optgroup %s>' % uilib.sgml_attributes(oattrs))
                 optgroup_opened = True
             elif value in curvalues:
-                options.append(tags.option(label, value=value, selected='selected'))
+                options.append(tags.option(label, value=value,
+                                           selected='selected', **oattrs))
             else:
-                options.append(tags.option(label, value=value))
+                options.append(tags.option(label, value=value, **oattrs))
         if optgroup_opened:
             options.append(u'</optgroup>')
         return tags.select(name=name, multiple=self._multiple,
@@ -214,20 +223,26 @@
     type = 'checkbox'
     vocabulary_widget = True
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         name, curvalues, attrs = self._render_attrs(form, field)
         domid = attrs.pop('id', None)
-        sep = attrs.pop('separator', u'<br/>')
+        sep = attrs.pop('separator', u'<br/>\n')
         options = []
-        for i, (label, value) in enumerate(field.vocabulary(form)):
+        for i, option in enumerate(field.vocabulary(form)):
+            try:
+                label, value, oattrs = option
+            except ValueError:
+                label, value = option
+                oattrs = {}
             iattrs = attrs.copy()
+            iattrs.update(oattrs)
             if i == 0 and domid is not None:
-                iattrs['id'] = domid
+                iattrs.setdefault('id', domid)
             if value in curvalues:
                 iattrs['checked'] = u'checked'
             tag = tags.input(name=name, type=self.type, value=value, **iattrs)
-            options.append(tag + label + sep)
-        return '\n'.join(options)
+            options.append(tag + label)
+        return sep.join(options)
 
 
 class Radio(CheckBox):
@@ -237,6 +252,51 @@
     type = 'radio'
 
 
+# compound widgets #############################################################
+
+class IntervalWidget(FieldWidget):
+    """custom widget to display an interval composed by 2 fields. This widget
+    is expected to be used with a CompoundField containing the two actual
+    fields.
+
+    Exemple usage::
+
+from uicfg import autoform_field, autoform_section
+autoform_field.tag_attribute(('Concert', 'minprice'),
+                              CompoundField(fields=(IntField(name='minprice'),
+                                                    IntField(name='maxprice')),
+                                            label=_('price'),
+                                            widget=IntervalWidget()
+                                            ))
+# we've to hide the other field manually for now
+autoform_section.tag_attribute(('Concert', 'maxprice'), 'generated')
+    """
+    def render(self, form, field, renderer):
+        actual_fields = field.fields
+        assert len(actual_fields) == 2
+        return u'<div>%s %s %s %s</div>' % (
+            form.req._('from_interval_start'),
+            actual_fields[0].render(form, renderer),
+            form.req._('to_interval_end'),
+            actual_fields[1].render(form, renderer),
+            )
+
+
+class HorizontalLayoutWidget(FieldWidget):
+    """custom widget to display a set of fields grouped together horizontally
+    in a form. See `IntervalWidget` for example usage.
+    """
+    def render(self, form, field, renderer):
+        if self.attrs.get('display_label', True):
+            subst = self.attrs.get('label_input_substitution', '%(label)s %(input)s')
+            fields = [subst % {'label': renderer.render_label(form, f),
+                              'input': f.render(form, renderer)}
+                      for f in field.subfields(form)]
+        else:
+            fields = [f.render(form, renderer) for f in field.subfields(form)]
+        return u'<div>%s</div>' % ' '.join(fields)
+
+
 # javascript widgets ###########################################################
 
 class DateTimePicker(TextInput):
@@ -262,8 +322,8 @@
         req.html_headers.define_var('MONTHNAMES', monthnames)
         req.html_headers.define_var('DAYNAMES', daynames)
 
-    def render(self, form, field):
-        txtwidget = super(DateTimePicker, self).render(form, field)
+    def render(self, form, field, renderer):
+        txtwidget = super(DateTimePicker, self).render(form, field, renderer)
         self.add_localized_infos(form.req)
         cal_button = self._render_calendar_popup(form, field)
         return txtwidget + cal_button
@@ -302,7 +362,7 @@
         if inputid is not None:
             self.attrs['cubicweb:inputid'] = inputid
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         self.add_media(form)
         attrs = self._render_attrs(form, field)[-1]
         return tags.div(**attrs)
@@ -373,8 +433,8 @@
         etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]
         attrs['cubicweb:etype_from'] = etype_from
 
-    def render(self, form, field):
-        return super(AddComboBoxWidget, self).render(form, field) + u'''
+    def render(self, form, field, renderer):
+        return super(AddComboBoxWidget, self).render(form, field, renderer) + u'''
 <div id="newvalue">
   <input type="text" id="newopt" />
   <a href="javascript:noop()" id="add_newopt">&nbsp;</a></div>
@@ -400,7 +460,7 @@
         self.cwaction = cwaction
         self.attrs.setdefault('klass', 'validateButton')
 
-    def render(self, form, field=None):
+    def render(self, form, field=None, renderer=None):
         label = form.req._(self.label)
         attrs = self.attrs.copy()
         if self.cwaction:
@@ -443,7 +503,7 @@
         self.imgressource = imgressource
         self.label = label
 
-    def render(self, form, field=None):
+    def render(self, form, field=None, renderer=None):
         label = form.req._(self.label)
         imgsrc = form.req.external_resource(self.imgressource)
         return '<a id="%(domid)s" href="%(href)s">'\
--- a/web/request.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/request.py	Fri Jul 31 14:25:30 2009 +0200
@@ -26,6 +26,7 @@
 from cubicweb.common.mail import header
 from cubicweb.common.uilib import remove_html_tags
 from cubicweb.utils import SizeConstrainedList, HTMLHead
+from cubicweb.view import STRICT_DOCTYPE
 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
                           RequestError, StatusResponse)
 
@@ -80,7 +81,7 @@
         # to create a relation with another)
         self.search_state = ('normal',)
         # tabindex generator
-        self.tabindexgen = count()
+        self.tabindexgen = count(1)
         self.next_tabindex = self.tabindexgen.next
         # page id, set by htmlheader template
         self.pageid = None
@@ -515,7 +516,7 @@
         return self.base_url() + self.relative_path(includeparams)
 
     def _datadir_url(self):
-        """return url of the application's data directory"""
+        """return url of the instance's data directory"""
         return self.base_url() + 'data%s/' % self.vreg.config.instance_md5_version()
 
     def selected(self, url):
@@ -587,7 +588,7 @@
 
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
-        to the application's root, but some other normalization may be needed
+        to the instance's root, but some other normalization may be needed
         so that the returned path may be used to compare to generated urls
 
         :param includeparams:
@@ -699,6 +700,14 @@
         return useragent and 'MSIE' in useragent
 
     def xhtml_browser(self):
+        """return True if the browser is considered as xhtml compatible.
+
+        If the instance is configured to always return text/html and not
+        application/xhtml+xml, this method will always return False, even though
+        this is semantically different
+        """
+        if self.vreg.config['force-html-content-type']:
+            return False
         useragent = self.useragent()
         # * MSIE/Konqueror does not support xml content-type
         # * Opera supports xhtml and handles namespaces properly but it breaks
@@ -713,5 +722,11 @@
             return 'application/xhtml+xml'
         return 'text/html'
 
+    def document_surrounding_div(self):
+        if self.xhtml_browser():
+            return (u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE +
+                    u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
+        return u'<div>'
+
 from cubicweb import set_log_methods
 set_log_methods(CubicWebRequestBase, LOGGER)
--- a/web/test/data/schema/testschema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/data/schema/testschema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,6 +5,11 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
+
+from yams.buildobjs import (EntityType, RelationDefinition, RelationType, String,
+                            Int, SubjectRelation)
+from yams.constraints import IntervalBoundConstraint
+
 class Salesterm(EntityType):
     described_by_test = SubjectRelation('File', cardinality='1*', composite='subject')
     amount = Int(constraints=[IntervalBoundConstraint(0, 100)])
--- a/web/test/unittest_form.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_form.py	Fri Jul 31 14:25:30 2009 +0200
@@ -91,7 +91,7 @@
         e.req = self.req
         geid = self.execute('CWGroup X WHERE X name "users"')[0][0]
         self.req.form['__linkto'] = 'in_group:%s:subject' % geid
-        form = self.vreg.select_object('forms', 'edition', self.req, None, entity=e)
+        form = self.vreg.select('forms', 'edition', self.req, entity=e)
         form.content_type = 'text/html'
         pageinfo = self._check_html(form.form_render(), form, template=None)
         inputs = pageinfo.find_tag('select', False)
@@ -161,7 +161,7 @@
 
     def test_richtextfield_2(self):
         self.req.use_fckeditor = lambda: True
-        self._test_richtextfield('<input name="description_format:%(eid)s" style="display: block" type="hidden" value="text/rest"></input><textarea cols="80" cubicweb:type="wysiwyg" id="description:%(eid)s" name="description:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="0"></textarea>')
+        self._test_richtextfield('<input name="description_format:%(eid)s" style="display: block" type="hidden" value="text/rest" /><textarea cols="80" cubicweb:type="wysiwyg" id="description:%(eid)s" name="description:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="0"></textarea>')
 
 
     def test_filefield(self):
@@ -172,14 +172,14 @@
                                data=Binary('new widgets system'))
         form = FFForm(self.req, redirect_path='perdu.com', entity=file)
         self.assertTextEquals(self._render_entity_field('data', form),
-                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value=""></input>
+                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value="" />
 <a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
 <div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="1" type="text" value="text/plain"></input><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="2" type="text" value="UTF-8"></input><br/>
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="1" type="text" value="text/plain" /><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="2" type="text" value="UTF-8" /><br/>
 </div>
 <br/>
-<input name="data:%(eid)s__detach" type="checkbox"></input>
+<input name="data:%(eid)s__detach" type="checkbox" />
 detach attached file
 ''' % {'eid': file.eid})
 
@@ -196,14 +196,14 @@
                                data=Binary('new widgets system'))
         form = EFFForm(self.req, redirect_path='perdu.com', entity=file)
         self.assertTextEquals(self._render_entity_field('data', form),
-                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value=""></input>
+                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="0" type="file" value="" />
 <a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
 <div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="1" type="text" value="text/plain"></input><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="2" type="text" value="UTF-8"></input><br/>
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="1" type="text" value="text/plain" /><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="2" type="text" value="UTF-8" /><br/>
 </div>
 <br/>
-<input name="data:%(eid)s__detach" type="checkbox"></input>
+<input name="data:%(eid)s__detach" type="checkbox" />
 detach attached file
 <p><b>You can either submit a new file using the browse button above, or choose to remove already uploaded file by checking the "detach attached file" check-box, or edit file content online with the widget below.</b></p>
 <textarea cols="80" name="data:%(eid)s" onkeyup="autogrow(this)" rows="3" tabindex="3">new widgets system</textarea>''' % {'eid': file.eid})
@@ -214,9 +214,9 @@
             upassword = StringField(widget=PasswordInput)
         form = PFForm(self.req, redirect_path='perdu.com', entity=self.entity)
         self.assertTextEquals(self._render_entity_field('upassword', form),
-                              '''<input id="upassword:%(eid)s" name="upassword:%(eid)s" tabindex="0" type="password" value="__cubicweb_internal_field__"></input>
+                              '''<input id="upassword:%(eid)s" name="upassword:%(eid)s" tabindex="0" type="password" value="__cubicweb_internal_field__" />
 <br/>
-<input name="upassword-confirm:%(eid)s" tabindex="0" type="password" value="__cubicweb_internal_field__"></input>
+<input name="upassword-confirm:%(eid)s" tabindex="0" type="password" value="__cubicweb_internal_field__" />
 &nbsp;
 <span class="emphasis">confirm password</span>''' % {'eid': self.entity.eid})
 
--- a/web/test/unittest_magicsearch.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_magicsearch.py	Fri Jul 31 14:25:30 2009 +0200
@@ -44,7 +44,7 @@
         super(QueryTranslatorTC, self).setUp()
         self.req = self.env.create_request()
         self.vreg.config.translations = {'en': _translate}
-        proc = self.vreg.select_component('magicsearch', self.req)
+        proc = self.vreg.select('components', 'magicsearch', self.req)
         self.proc = [p for p in proc.processors if isinstance(p, QueryTranslator)][0]
 
     def test_basic_translations(self):
@@ -69,7 +69,7 @@
         super(QSPreProcessorTC, self).setUp()
         self.vreg.config.translations = {'en': _translate}
         self.req = self.request()
-        proc = self.vreg.select_component('magicsearch', self.req)
+        proc = self.vreg.select('components', 'magicsearch', self.req)
         self.proc = [p for p in proc.processors if isinstance(p, QSPreProcessor)][0]
         self.proc.req = self.req
 
@@ -191,7 +191,7 @@
         super(ProcessorChainTC, self).setUp()
         self.vreg.config.translations = {'en': _translate}
         self.req = self.request()
-        self.proc = self.vreg.select_component('magicsearch', self.req)
+        self.proc = self.vreg.select('components', 'magicsearch', self.req)
 
     def test_main_preprocessor_chain(self):
         """tests QUERY_PROCESSOR"""
--- a/web/test/unittest_urlrewrite.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_urlrewrite.py	Fri Jul 31 14:25:30 2009 +0200
@@ -29,12 +29,14 @@
         self.assertListEquals(rules, [
             ('foo' , dict(rql='Foo F')),
             ('/index' , dict(vid='index2')),
-            ('/schema', {'vid': 'schema'}),
+            ('/schema', dict(vid='schema')),
             ('/myprefs', dict(vid='propertiesform')),
             ('/siteconfig', dict(vid='systempropertiesform')),
+            ('/siteinfo', dict(vid='info')),
             ('/manage', dict(vid='manage')),
-            ('/notfound', {'vid': '404'}),
-            ('/error', {'vid': 'error'}),
+            ('/notfound', dict(vid='404')),
+            ('/error', dict(vid='error')),
+            ('/sparql', dict(vid='sparql')),
             ('/schema/([^/]+?)/?$', {'rql': r'Any X WHERE X is CWEType, X name "\1"', 'vid': 'eschema'}),
             ('/add/([^/]+?)/?$' , dict(vid='creation', etype=r'\1')),
             ('/doc/images/(.+?)/?$', dict(fid='\\1', vid='wdocimages')),
--- a/web/test/unittest_views_actions.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_actions.py	Fri Jul 31 14:25:30 2009 +0200
@@ -13,19 +13,19 @@
     def test_view_action(self):
         req = self.request(__message='bla bla bla', vid='rss', rql='CWUser X')
         rset = self.execute('CWUser X')
-        vaction = [action for action in self.vreg.possible_vobjects('actions', req, rset)
+        vaction = [action for action in self.vreg.possible_vobjects('actions', req, rset=rset)
                    if action.id == 'view'][0]
         self.assertEquals(vaction.url(), 'http://testing.fr/cubicweb/view?rql=CWUser%20X')
 
     def test_sendmail_action(self):
         req = self.request()
         rset = self.execute('Any X WHERE X login "admin"', req=req)
-        self.failUnless([action for action in self.vreg.possible_vobjects('actions', req, rset)
+        self.failUnless([action for action in self.vreg.possible_vobjects('actions', req, rset=rset)
                          if action.id == 'sendemail'])
         self.login('anon')
         req = self.request()
         rset = self.execute('Any X WHERE X login "anon"', req=req)
-        self.failIf([action for action in self.vreg.possible_vobjects('actions', req, rset)
+        self.failIf([action for action in self.vreg.possible_vobjects('actions', req, rset=rset)
                      if action.id == 'sendemail'])
 
 if __name__ == '__main__':
--- a/web/test/unittest_views_basecontrollers.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Fri Jul 31 14:25:30 2009 +0200
@@ -8,16 +8,13 @@
 import simplejson
 
 from logilab.common.testlib import unittest_main, mock_object
-
-from cubicweb import Binary, Unauthorized
-from cubicweb.devtools._apptest import TestEnvironment
 from cubicweb.devtools.apptest import EnvBasedTC, ControllerTC
 
-from cubicweb.common import ValidationError
+from cubicweb import Binary, NoSelectableObject, ValidationError
+from cubicweb.view import STRICT_DOCTYPE
 from cubicweb.common.uilib import rql_for_eid
 
 from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError
-from cubicweb.web.views.basecontrollers import xhtml_wrap
 
 from cubicweb.entities.authobjs import CWUser
 
@@ -499,7 +496,7 @@
         # updated (which is what happened before this test)
         req = self.request()
         req.form['url'] = 'http://intranet.logilab.fr/'
-        controller = self.env.app.select_controller('embed', req)
+        controller = self.vreg.select('controllers', 'embed', req)
         result = controller.publish(rset=None)
 
 
@@ -507,7 +504,7 @@
 
     def test_usable_by_guets(self):
         req = self.request()
-        self.env.app.select_controller('reportbug', req)
+        self.vreg.select('controllers', 'reportbug', req)
 
 
 class SendMailControllerTC(EnvBasedTC):
@@ -515,7 +512,7 @@
     def test_not_usable_by_guets(self):
         self.login('anon')
         req = self.request()
-        self.assertRaises(Unauthorized, self.env.app.select_controller, 'sendmail', req)
+        self.assertRaises(NoSelectableObject, self.env.vreg.select, 'controllers', 'sendmail', req)
 
 
 
@@ -523,7 +520,7 @@
 
     def ctrl(self, req=None):
         req = req or self.request(url='http://whatever.fr/')
-        return self.env.app.select_controller('json', req)
+        return self.vreg.select('controllers', 'json', req)
 
     def setup_database(self):
         self.pytag = self.add_entity('Tag', name=u'python')
@@ -538,8 +535,13 @@
         ctrl = self.ctrl(req)
         rset = self.john.as_rset()
         rset.req = req
-        self.assertTextEquals(ctrl.publish(),
-                              xhtml_wrap(mock_object(req=req), ctrl.view('primary', rset)))
+        source = ctrl.publish()
+        self.failUnless(source.startswith('<?xml version="1.0"?>\n' + STRICT_DOCTYPE +
+                                          u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
+                        )
+        req.xhtml_browser = lambda: False
+        source = ctrl.publish()
+        self.failUnless(source.startswith('<div>'))
 
 #     def test_json_exec(self):
 #         rql = 'Any T,N WHERE T is Tag, T name N'
--- a/web/test/unittest_views_baseforms.py	Fri Jul 31 14:18:53 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-"""cubicweb.web.views.baseforms unit tests
-
-:organization: Logilab
-:copyright: 2001-2009 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
-"""
-
-from StringIO import StringIO
-from datetime import date
-import re
-
-
-from logilab.common.testlib import unittest_main
-from logilab.common.decorators import clear_cache
-from cubicweb.devtools.apptest import EnvBasedTC
-from cubicweb.entities import AnyEntity
-from cubicweb.web import widgets
-
-orig_now = widgets.datetime.now
-
-def setup_module(options):
-    def _today():
-        return date(0000, 1, 1)
-    widgets.datetime.now = _today
-
-def teardown_module(options, results):
-    widgets.datetime.now = orig_now
-
-
-def cleanup_text(text):
-    return re.sub('\d\d:\d\d', 'hh:mm', re.sub('\d+/\d\d/\d\d', 'YYYY/MM/DD', '\n'.join(l.strip() for l in text.splitlines() if l.strip())))
-
-
-
-class EditionFormTC(EnvBasedTC):
-
-    def setup_database(self):
-        self.create_user('joe')
-
-    def _build_creation_form(self, etype):
-        req = self.request()
-        req.next_tabindex()
-        req.next_tabindex()
-        req.del_page_data()
-        req.form['etype'] = etype
-        view = self.vreg.select_view('creation', req, None)
-        entity = self.vreg.etype_class(etype)(req, None, None)
-        buffer = StringIO()
-        view.w = buffer.write
-        view.edit_form(entity, {})
-        return buffer.getvalue()
-
-    def _test_view_for(self, etype, expected):
-        self.assertTextEquals(expected, cleanup_text(self._build_creation_form(etype)))
-
-    def test_base(self):
-        self._test_view_for('CWGroup', '''\
-<form id="entityForm" class="entityForm" cubicweb:target="eformframe"
-method="post" onsubmit="return freezeFormButtons('entityForm')" enctype="application/x-www-form-urlencoded" action="http://testing.fr/cubicweb/validateform">
-<div class="formTitle"><span>egroup (creation)</span></div>
-<div id="progress">validating...</div>
-<div class="iformTitle"><span>main informations</span></div>
-<div class="formBody"><fieldset>
-<input type="hidden" name="eid" value="A" />
-<input type="hidden" name="__type:A" value="CWGroup" />
-<input type="hidden" name="__maineid" value="A" />
-<input id="errorurl" type="hidden" name="__errorurl" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop" />
-<input type="hidden" name="__form_id" value="edition" />
-<input type="hidden" name="__message" value="element created" />
-<table id="entityFormA" class="attributeForm" style="width:100%;">
-<tr>
-<th class="labelCol"><label class="required" for="name:A">name</label></th>
-<td style="width:100%;">
-<input type="hidden" name="edits-name:A" value="__cubicweb_internal_field__"/>
-<input type="text" name="name:A" value="" accesskey="n" id="name:A" maxlength="64" size="40" tabindex="2"/>
-<br/>
-</td>
-</tr>
-</table>
-</fieldset>
-</div>
-<table width="100%">
-<tbody>
-<tr><td align="center">
-<input class="validateButton" type="submit" name="defaultsubmit" value="Button_ok" tabindex="3"/>
-</td><td style="align: right; width: 50%;">
-<input class="validateButton" type="button" onclick="postForm('__action_apply', 'Button_apply', 'entityForm')" value="Button_apply" tabindex="4"/>
-<input class="validateButton" type="button" onclick="postForm('__action_cancel', 'Button_cancel', 'entityForm')" value="Button_cancel" tabindex="5"/>
-</td></tr>
-</tbody>
-</table>
-</form>''')
-
-    def test_with_inline_view(self):
-        activated = self.execute('Any X WHERE X is State, X name "activated"')[0][0]
-        self._test_view_for('CWUser', '''<form id="entityForm" class="entityForm" cubicweb:target="eformframe"
-method="post" onsubmit="return freezeFormButtons('entityForm')" enctype="application/x-www-form-urlencoded" action="http://testing.fr/cubicweb/validateform">
-<div class="formTitle"><span>euser (creation)</span></div>
-<div id="progress">validating...</div>
-<div class="iformTitle"><span>main informations</span></div>
-<div class="formBody"><fieldset>
-<input type="hidden" name="eid" value="A" />
-<input type="hidden" name="__type:A" value="CWUser" />
-<input type="hidden" name="__maineid" value="A" />
-<input id="errorurl" type="hidden" name="__errorurl" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop" />
-<input type="hidden" name="__form_id" value="edition" />
-<input type="hidden" name="__message" value="element created" />
-<table id="entityFormA" class="attributeForm" style="width:100%%;">
-<tr>
-<th class="labelCol"><label class="required" for="login:A">login</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-login:A" value="__cubicweb_internal_field__"/>
-<input type="text" name="login:A" value="" accesskey="l" id="login:A" maxlength="64" size="40" tabindex="2"/>
-<br/>&nbsp;<span class="helper">unique identifier used to connect to the application</span>
-</td>
-</tr>
-<tr>
-<th class="labelCol"><label class="required" for="upassword:A">upassword</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-upassword:A" value="__cubicweb_internal_field__"/>
-<input type="password" name="upassword:A" value="" accesskey="u" id="upassword:A" tabindex="3"/><br/>
-<input type="password" name="upassword-confirm:A" id="upassword-confirm:A" tabindex="4"/>&nbsp;<span class="emphasis">(confirm password)</span>
-<br/>
-</td>
-</tr>
-<tr>
-<th class="labelCol"><label for="firstname:A">firstname</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-firstname:A" value="__cubicweb_internal_field__"/>
-<input type="text" name="firstname:A" value="" accesskey="f" id="firstname:A" maxlength="64" size="40" tabindex="5"/>
-<br/>
-</td>
-</tr>
-<tr>
-<th class="labelCol"><label for="surname:A">surname</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-surname:A" value="__cubicweb_internal_field__"/>
-<input type="text" name="surname:A" value="" accesskey="s" id="surname:A" maxlength="64" size="40" tabindex="6"/>
-<br/>
-</td>
-</tr>
-<tr>
-<th class="labelCol"><label class="required" for="in_group:A">in_group</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-in_group:A" value="__cubicweb_internal_field__"/>
-<select name="in_group:A" id="in_group:A" multiple="multiple" size="5" tabindex="7">
-<option value="3" >guests</option>
-<option value="1" >managers</option>
-<option value="2" >users</option>
-</select>
-<br/>&nbsp;<span class="helper">groups grant permissions to the user</span>
-</td>
-</tr>
-<tr>
-<th class="labelCol"><label class="required" for="in_state:A">in_state</label></th>
-<td style="width:100%%;">
-<input type="hidden" name="edits-in_state:A" value="__cubicweb_internal_field__"/>
-<select name="in_state:A" id="in_state:A" tabindex="8">
-<option value="%(activated)s" >activated</option>
-</select>
-<br/>&nbsp;<span class="helper">account state</span>
-</td>
-</tr>
-</table>
-<div id="inlineuse_emailslot">
-<div class="inlinedform" id="addNewEmailAddressuse_emailsubject:A" cubicweb:limit="true">
-<a class="addEntity" id="adduse_email:Alink" href="javascript: addInlineCreationForm('A', 'CWUser', 'EmailAddress', 'use_email', 'subject')" >+ add a EmailAddress.</a>
-</div>
-<div class="trame_grise">&nbsp;</div>
-</div>
-</fieldset>
-</div>
-<table width="100%%">
-<tbody>
-<tr><td align="center">
-<input class="validateButton" type="submit" name="defaultsubmit" value="Button_ok" tabindex="9"/>
-</td><td style="align: right; width: 50%%;">
-<input class="validateButton" type="button" onclick="postForm('__action_apply', 'Button_apply', 'entityForm')" value="Button_apply" tabindex="10"/>
-<input class="validateButton" type="button" onclick="postForm('__action_cancel', 'Button_cancel', 'entityForm')" value="Button_cancel" tabindex="11"/>
-</td></tr>
-</tbody>
-</table>
-</form>''' % {'activated' : activated})
-
-    def test_redirection_after_creation(self):
-        req = self.request()
-        req.form['etype'] = 'CWUser'
-        view = self.vreg.select_view('creation', req, None)
-        self.assertEquals(view.redirect_url(), 'http://testing.fr/cubicweb/euser')
-        req.form['__redirectrql'] = 'Any X WHERE X eid 3012'
-        req.form['__redirectvid'] = 'avid'
-        self.assertEquals(view.redirect_url(), 'http://testing.fr/cubicweb/view?rql=Any%20X%20WHERE%20X%20eid%203012&vid=avid')
-
-
-    def test_need_multipart(self):
-        req = self.request()
-        class Salesterm(AnyEntity):
-            id = 'Salesterm'
-            __rtags__ = {'described_by_test' : 'inlineview'}
-        vreg = self.vreg
-        vreg.register_vobject_class(Salesterm)
-        req.form['etype'] = 'Salesterm'
-        entity = vreg.etype_class('Salesterm')(req, None, None)
-        view = vreg.select_view('creation', req, None)
-        self.failUnless(view.need_multipart(entity))
-
-
-
-    def test_nonregr_check_add_permission_on_relation(self):
-        from cubes.blog.entities import BlogEntry
-        class BlogEntryPlus(BlogEntry):
-            __rtags__ = {'checked_by': 'primary'}
-        self.vreg.register_vobject_class(BlogEntryPlus)
-        clear_cache(self.vreg, 'etype_class')
-        # an admin should be able to edit the checked_by relation
-        html = self._build_creation_form('BlogEntry')
-        self.failUnless('name="edits-checked_by:A"' in html)
-        # a regular user should not be able to see the relation
-        self.login('joe')
-        html = self._build_creation_form('BlogEntry')
-        self.failIf('name="edits-checked_by:A"' in html)
-
-from cubicweb.devtools.testlib import WebTest
-from cubicweb.devtools.htmlparser import DTDValidator
-
-class CopyWebTest(WebTest):
-
-    def setup_database(self):
-        p = self.create_user("Doe")
-        # do not try to skip 'primary_email' for this test
-        e = self.add_entity('EmailAddress', address=u'doe@doe.com')
-        self.execute('SET P use_email E, P primary_email E WHERE P eid %(p)s, E eid %(e)s',
-                     {'p' : p.eid, 'e' : e.eid})
-
-
-    def test_cloned_elements_in_copy_form(self):
-        rset = self.execute('CWUser P WHERE P login "Doe"')
-        output = self.view('copy', rset)
-        clones = [attrs for _, attrs in output.input_tags
-                  if attrs.get('name', '').startswith('__cloned_eid')]
-        # the only cloned entity should be the original person
-        self.assertEquals(len(clones), 1)
-        attrs = clones[0]
-        self.assertEquals(attrs['name'], '__cloned_eid:A')
-        self.assertEquals(int(attrs['value']), rset[0][0])
-
-
-if __name__ == '__main__':
-    unittest_main()
--- a/web/test/unittest_views_baseviews.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_baseviews.py	Fri Jul 31 14:25:30 2009 +0200
@@ -86,7 +86,7 @@
         rset = self.execute('Any X, D, CD, NOW - CD WHERE X is State, X description D, X creation_date CD, X eid %(x)s',
                             {'x': e.eid}, 'x')
         req = self.request()
-        view = self.vreg.select_view('table', req, rset)
+        view = self.vreg.select('views', 'table', req, rset)
         return e, rset, view
 
     def test_headers(self):
--- a/web/test/unittest_views_editforms.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_editforms.py	Fri Jul 31 14:25:30 2009 +0200
@@ -18,8 +18,8 @@
     def test_custom_widget(self):
         AEF.rfields_kwargs.tag_subject_of(('CWUser', 'login', '*'),
                                           {'widget':AutoCompletionWidget})
-        form = self.vreg.select_object('forms', 'edition', self.request(), None,
-                                       entity=self.user())
+        form = self.vreg.select('forms', 'edition', self.request(),
+                                entity=self.user())
         field = form.field_by_name('login')
         self.assertIsInstance(field.widget, AutoCompletionWidget)
         AEF.rfields_kwargs.del_rtag('CWUser', 'login', '*', 'subject')
@@ -114,11 +114,11 @@
 
     def test_edition_form(self):
         rset = self.execute('CWUser X LIMIT 1')
-        form = self.vreg.select_object('forms', 'edition', rset.req, rset,
-                                       row=0, col=0)
+        form = self.vreg.select('forms', 'edition', rset.req, rset=rset,
+                                row=0, col=0)
         # should be also selectable by specifying entity
-        self.vreg.select_object('forms', 'edition', self.request(), None,
-                                entity=rset.get_entity(0, 0))
+        self.vreg.select('forms', 'edition', rset.req,
+                         entity=rset.get_entity(0, 0))
         self.failIf(any(f for f in form.fields if f is None))
 
 
--- a/web/test/unittest_views_navigation.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_navigation.py	Fri Jul 31 14:25:30 2009 +0200
@@ -19,31 +19,31 @@
     def test_navigation_selection(self):
         rset = self.execute('Any X,N WHERE X name N')
         req = self.request()
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, PageNavigation)
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, PageNavigation)
         req.set_search_state('normal')
         rset = self.execute('Any X,N ORDERBY N WHERE X name N')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, SortedNavigation)
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, SortedNavigation)
         req.set_search_state('normal')
         rset = self.execute('Any X,N LIMIT 10 WHERE X name N')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select_object('components', 'navigation', req, rset=rset)
         self.assertEquals(navcomp, None)
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select_object('components', 'navigation', req, rset=rset)
         self.assertEquals(navcomp, None)
         req.set_search_state('normal')
         rset = self.execute('Any N, COUNT(RDEF) GROUPBY N ORDERBY N WHERE RDEF relation_type RT, RT name N')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, SortedNavigation)
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         self.assertIsInstance(navcomp, SortedNavigation)
 
 
@@ -51,22 +51,22 @@
         rset = self.execute('Any X,N ORDERBY N WHERE X name N')
         req = self.request()
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg.select_component('navigation', rset.req, rset)
+        navcomp = self.vreg.select('components', 'navigation', rset.req, rset=rset)
         html = navcomp.render()
         rset = self.execute('Any RDEF ORDERBY RT WHERE RDEF relation_type RT')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         html = navcomp.render()
         rset = self.execute('Any RDEF ORDERBY RDEF WHERE RDEF relation_type RT')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         html = navcomp.render()
         rset = self.execute('CWAttribute RDEF ORDERBY RDEF')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         html = navcomp.render()
         rset = self.execute('Any RDEF ORDERBY N WHERE RDEF relation_type RT, RT name N')
-        navcomp = self.vreg.select_component('navigation', req, rset)
+        navcomp = self.vreg.select('components', 'navigation', req, rset=rset)
         html = navcomp.render()
         rset = self.execute('Any N, COUNT(RDEF) GROUPBY N ORDERBY N WHERE RDEF relation_type RT, RT name N')
-        navcomp = self.vreg.select_component('navigation', rset.req, rset)
+        navcomp = self.vreg.select('components', 'navigation', rset.req, rset=rset)
         html = navcomp.render()
 
 
@@ -77,12 +77,12 @@
         view = mock_object(is_primary=lambda x: True)
         rset = self.execute('CWUser X LIMIT 1')
         req = self.request()
-        objs = self.vreg.possible_vobjects('contentnavigation', req, rset,
+        objs = self.vreg.possible_vobjects('contentnavigation', req, rset=rset,
                                            view=view, context='navtop')
         # breadcrumbs should be in headers by default
         clsids = set(obj.id for obj in objs)
         self.failUnless('breadcrumbs' in clsids)
-        objs = self.vreg.possible_vobjects('contentnavigation', req, rset,
+        objs = self.vreg.possible_vobjects('contentnavigation', req, rset=rset,
                                           view=view, context='navbottom')
         # breadcrumbs should _NOT_ be in footers by default
         clsids = set(obj.id for obj in objs)
@@ -91,12 +91,12 @@
                      'P value "navbottom"')
         # breadcrumbs should now be in footers
         req.cnx.commit()
-        objs = self.vreg.possible_vobjects('contentnavigation', req, rset,
+        objs = self.vreg.possible_vobjects('contentnavigation', req, rset=rset,
                                           view=view, context='navbottom')
 
         clsids = [obj.id for obj in objs]
         self.failUnless('breadcrumbs' in clsids)
-        objs = self.vreg.possible_vobjects('contentnavigation', req, rset,
+        objs = self.vreg.possible_vobjects('contentnavigation', req, rset=rset,
                                           view=view, context='navtop')
 
         clsids = [obj.id for obj in objs]
--- a/web/test/unittest_views_pyviews.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_views_pyviews.py	Fri Jul 31 14:25:30 2009 +0200
@@ -4,9 +4,9 @@
 class PyViewsTC(EnvBasedTC):
 
     def test_pyvaltable(self):
-        content = self.vreg.view('pyvaltable', self.request(),
-                                 pyvalue=[[1, 'a'], [2, 'b']],
-                                 headers=['num', 'char'])
+        content = self.vreg.render('pyvaltable', self.request(),
+                                   pyvalue=[[1, 'a'], [2, 'b']],
+                                   headers=['num', 'char'])
         self.assertEquals(content.strip(), '''<table class="listing">
 <tr><th>num</th><th>char</th></tr>
 <tr><td>1</td><td>a</td></tr>
@@ -14,8 +14,8 @@
 </table>''')
 
     def test_pyvallist(self):
-        content = self.vreg.view('pyvallist', self.request(),
-                                 pyvalue=[1, 'a'])
+        content = self.vreg.render('pyvallist', self.request(),
+                                   pyvalue=[1, 'a'])
         self.assertEquals(content.strip(), '''<ul>
 <li>1</li>
 <li>a</li>
--- a/web/test/unittest_viewselector.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/test/unittest_viewselector.py	Fri Jul 31 14:25:30 2009 +0200
@@ -22,8 +22,9 @@
                ('logout', actions.LogoutAction)]
 SITEACTIONS = [('siteconfig', actions.SiteConfigurationAction),
                ('manage', actions.ManageAction),
-               ('schema', schema.ViewSchemaAction)]
-
+               ('schema', schema.ViewSchemaAction),
+               ('siteinfo', actions.SiteInfoAction),
+               ]
 
 class ViewSelectorTC(EnvBasedTC):
 
@@ -201,6 +202,7 @@
                               ('text', baseviews.TextView),
                               ('treeview', treeview.TreeView),
                               ('vcard', vcard.VCardCWUserView),
+                              ('wfhistory', workflow.WFHistoryView),
                               ('xbel', xbel.XbelView),
                               ('xml', xmlrss.XMLView),
                               ])
@@ -262,7 +264,7 @@
         req = self.request()
         # creation form
         req.form['etype'] = 'CWGroup'
-        self.assertIsInstance(self.vreg.select_view('creation', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'creation', req, rset=rset),
                               editforms.CreationFormView)
         del req.form['etype']
         # custom creation form
@@ -271,94 +273,94 @@
         self.vreg._loadedmods[__name__] = {}
         self.vreg.register_vobject_class(CWUserCreationForm)
         req.form['etype'] = 'CWUser'
-        self.assertIsInstance(self.vreg.select_view('creation', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'creation', req, rset=rset),
                               CWUserCreationForm)
 
     def test_select_view(self):
         # no entity
         rset = None
         req = self.request()
-        self.assertIsInstance(self.vreg.select_view('index', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'index', req, rset=rset),
                              startup.IndexView)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'primary', req, rset)
+                             self.vreg.select, 'views', 'primary', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'table', req, rset)
+                             self.vreg.select, 'views', 'table', req, rset=rset)
 
         # no entity
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'index', req, rset)
+                              self.vreg.select, 'views', 'index', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'creation', req, rset)
+                              self.vreg.select, 'views', 'creation', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'primary', req, rset)
+                              self.vreg.select, 'views', 'primary', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'table', req, rset)
+                             self.vreg.select, 'views', 'table', req, rset=rset)
         # one entity
         rset, req = self.env.get_rset_and_req('CWGroup X WHERE X name "managers"')
-        self.assertIsInstance(self.vreg.select_view('primary', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'primary', req, rset=rset),
                              primary.PrimaryView)
-        self.assertIsInstance(self.vreg.select_view('list', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'list', req, rset=rset),
                              baseviews.ListView)
-        self.assertIsInstance(self.vreg.select_view('edition', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'edition', req, rset=rset),
                              editforms.EditionFormView)
-        self.assertIsInstance(self.vreg.select_view('table', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'table', req, rset=rset),
                              tableview.TableView)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'creation', req, rset)
+                              self.vreg.select, 'views', 'creation', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'index', req, rset)
+                              self.vreg.select, 'views', 'index', req, rset=rset)
         # list of entities of the same type
         rset, req = self.env.get_rset_and_req('CWGroup X')
-        self.assertIsInstance(self.vreg.select_view('primary', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'primary', req, rset=rset),
                              primary.PrimaryView)
-        self.assertIsInstance(self.vreg.select_view('list', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'list', req, rset=rset),
                              baseviews.ListView)
-        self.assertIsInstance(self.vreg.select_view('table', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'table', req, rset=rset),
                              tableview.TableView)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'creation', req, rset)
+                              self.vreg.select, 'views', 'creation', req, rset=rset)
         # list of entities of different types
         rset, req = self.env.get_rset_and_req('Any X')
-        self.assertIsInstance(self.vreg.select_view('primary', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'primary', req, rset=rset),
                                   primary.PrimaryView)
-        self.assertIsInstance(self.vreg.select_view('list', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'list', req, rset=rset),
                                   baseviews.ListView)
-        self.assertIsInstance(self.vreg.select_view('table', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'table', req, rset=rset),
                                   tableview.TableView)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'creation', req, rset)
+                             self.vreg.select, 'views', 'creation', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'index', req, rset)
+                              self.vreg.select, 'views', 'index', req, rset=rset)
         # whatever
         rset, req = self.env.get_rset_and_req('Any N, X WHERE X in_group Y, Y name N')
-        self.assertIsInstance(self.vreg.select_view('table', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'table', req, rset=rset),
                                   tableview.TableView)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'index', req, rset)
+                              self.vreg.select, 'views', 'index', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'creation', req, rset)
+                              self.vreg.select, 'views', 'creation', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'primary', req, rset)
+                             self.vreg.select, 'views', 'primary', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'list', req, rset)
+                             self.vreg.select, 'views', 'list', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                             self.vreg.select_view, 'edition', req, rset)
+                             self.vreg.select, 'views', 'edition', req, rset=rset)
         # mixed query
         rset, req = self.env.get_rset_and_req('Any U,G WHERE U is CWUser, G is CWGroup')
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'edition', req, rset)
+                              self.vreg.select, 'views', 'edition', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
-                              self.vreg.select_view, 'creation', req, rset)
-        self.assertIsInstance(self.vreg.select_view('table', req, rset),
+                              self.vreg.select, 'views', 'creation', req, rset=rset)
+        self.assertIsInstance(self.vreg.select('views', 'table', req, rset=rset),
                               tableview.TableView)
 
     def test_interface_selector(self):
         image = self.add_entity('Image', name=u'bim.png', data=Binary('bim'))
         # image primary view priority
         rset, req = self.env.get_rset_and_req('Image X WHERE X name "bim.png"')
-        self.assertIsInstance(self.vreg.select_view('primary', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'primary', req, rset=rset),
                               idownloadable.IDownloadablePrimaryView)
 
 
@@ -366,12 +368,12 @@
         image = self.add_entity('Image', name=u'bim.png', data=Binary('bim'))
         # image primary view priority
         rset, req = self.env.get_rset_and_req('Image X WHERE X name "bim.png"')
-        self.assertIsInstance(self.vreg.select_view('image', req, rset),
+        self.assertIsInstance(self.vreg.select('views', 'image', req, rset=rset),
                               idownloadable.ImageView)
         fileobj = self.add_entity('File', name=u'bim.txt', data=Binary('bim'))
         # image primary view priority
         rset, req = self.env.get_rset_and_req('File X WHERE X name "bim.txt"')
-        self.assertRaises(NoSelectableObject, self.vreg.select_view, 'image', req, rset)
+        self.assertRaises(NoSelectableObject, self.vreg.select, 'views', 'image', req, rset=rset)
 
 
 
@@ -382,7 +384,7 @@
         else:
             rset, req = self.env.get_rset_and_req(rql)
         try:
-            self.vreg.render('views', vid, req, rset=rset, **args)
+            self.vreg.render(vid, req, rset=rset, **args)
         except:
             print vid, rset, args
             raise
--- a/web/uicfg.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/uicfg.py	Fri Jul 31 14:25:30 2009 +0200
@@ -102,7 +102,7 @@
                                    init_primaryview_section,
                                    frozenset(('attributes', 'relations',
                                                'sideboxes', 'hidden')))
-for rtype in ('eid', 'creation_date', 'modification_date',
+for rtype in ('eid', 'creation_date', 'modification_date', 'cwuri',
               'is', 'is_instance_of', 'identity',
               'owned_by', 'created_by',
               'in_state', 'wf_info_for', 'require_permission',
@@ -113,9 +113,9 @@
 primaryview_section.tag_subject_of(('*', 'use_email', '*'), 'attributes')
 primaryview_section.tag_subject_of(('*', 'primary_email', '*'), 'hidden')
 
-for attr in ('name', 'meta', 'final'):
+for attr in ('name', 'final'):
     primaryview_section.tag_attribute(('CWEType', attr), 'hidden')
-for attr in ('name', 'meta', 'final', 'symetric', 'inlined'):
+for attr in ('name', 'final', 'symetric', 'inlined'):
     primaryview_section.tag_attribute(('CWRType', attr), 'hidden')
 
 
@@ -166,7 +166,11 @@
 # * 'schema'
 # * 'subobject' (not displayed by default)
 
-indexview_etype_section = {'EmailAddress': 'subobject'}
+indexview_etype_section = {'EmailAddress': 'subobject',
+                           'CWUser': 'system',
+                           'CWGroup': 'system',
+                           'CWPermission': 'system',
+                           }
 
 
 # autoform.AutomaticEntityForm configuration ##################################
@@ -182,7 +186,7 @@
             card = rschema.rproperty(sschema, oschema, 'cardinality')[1]
             composed = rschema.rproperty(sschema, oschema, 'composite') == 'subject'
         if sschema.is_metadata(rschema):
-            section = 'generated'
+            section = 'metadata'
         elif card in '1+':
             if not rschema.is_final() and composed:
                 section = 'generated'
@@ -202,6 +206,7 @@
 autoform_section.tag_attribute(('*', 'description'), 'secondary')
 autoform_section.tag_attribute(('*', 'creation_date'), 'metadata')
 autoform_section.tag_attribute(('*', 'modification_date'), 'metadata')
+autoform_section.tag_attribute(('*', 'cwuri'), 'metadata')
 autoform_section.tag_attribute(('*', 'has_text'), 'generated')
 autoform_section.tag_subject_of(('*', 'in_state', '*'), 'primary')
 autoform_section.tag_subject_of(('*', 'owned_by', '*'), 'metadata')
@@ -224,8 +229,8 @@
 autoform_section.tag_attribute(('CWUser', 'surname'), 'secondary')
 autoform_section.tag_attribute(('CWUser', 'last_login_time'), 'metadata')
 autoform_section.tag_subject_of(('CWUser', 'in_group', '*'), 'primary')
-autoform_section.tag_object_of(('*', 'owned_by', 'CWUser'), 'generated')
-autoform_section.tag_object_of(('*', 'created_by', 'CWUser'), 'generated')
+autoform_section.tag_object_of(('*', 'owned_by', 'CWUser'), 'metadata')
+autoform_section.tag_object_of(('*', 'created_by', 'CWUser'), 'metadata')
 autoform_section.tag_object_of(('*', 'bookmarked_by', 'CWUser'), 'metadata')
 autoform_section.tag_attribute(('Bookmark', 'path'), 'primary')
 autoform_section.tag_subject_of(('*', 'use_email', '*'), 'generated') # inlined actually
--- a/web/views/__init__.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/__init__.py	Fri Jul 31 14:25:30 2009 +0200
@@ -8,7 +8,7 @@
 __docformat__ = "restructuredtext en"
 
 import os
-from tempfile import mktemp
+import tempfile
 
 from rql import nodes
 
@@ -81,6 +81,8 @@
             if req.search_state[0] == 'normal':
                 return 'primary'
             return 'outofcontext-search'
+        if len(rset.column_types(0)) == 1:
+            return 'adaptedlist'
         return 'list'
     return 'table'
 
@@ -109,7 +111,7 @@
 
     def cell_call(self, row=0, col=0):
         self.row, self.col = row, col # in case one need it
-        tmpfile = mktemp('.png')
+        _, tmpfile = tempfile.mkstemp('.png')
         try:
             self._generate(tmpfile)
             self.w(open(tmpfile).read())
--- a/web/views/actions.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/actions.py	Fri Jul 31 14:25:30 2009 +0200
@@ -66,7 +66,7 @@
                 return 1
     return 0
 
-# generic primary actions #####################################################
+# generic 'main' actions #######################################################
 
 class SelectAction(Action):
     """base class for link search actions. By default apply on
@@ -146,7 +146,7 @@
         return self.build_url('view', rql=self.rset.rql, vid='muledit')
 
 
-# generic secondary actions ###################################################
+# generic "more" actions #######################################################
 
 class ManagePermissionsAction(Action):
     id = 'managepermission'
@@ -286,6 +286,12 @@
     title = _('manage')
     order = 20
 
+class SiteInfoAction(ManagersAction):
+    id = 'siteinfo'
+    title = _('info')
+    order = 30
+    __select__ = match_user_groups('users','managers')
+
 
 from logilab.common.deprecation import class_moved
 from cubicweb.web.views.bookmark import FollowAction
--- a/web/views/basecomponents.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/basecomponents.py	Fri Jul 31 14:25:30 2009 +0200
@@ -57,7 +57,7 @@
 
 
 class ApplLogo(component.Component):
-    """build the application logo, usually displayed in the header"""
+    """build the instance logo, usually displayed in the header"""
     id = 'logo'
     property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
@@ -118,8 +118,8 @@
 
 
 class ApplicationMessage(component.Component):
-    """display application's messages given using the __message parameter
-    into a special div section
+    """display messages given using the __message parameter into a special div
+    section
     """
     __select__ = yes()
     id = 'applmessages'
@@ -138,7 +138,7 @@
 
 
 class ApplicationName(component.Component):
-    """display the application name"""
+    """display the instance name"""
     id = 'appliname'
     property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
--- a/web/views/basecontrollers.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/basecontrollers.py	Fri Jul 31 14:25:30 2009 +0200
@@ -33,16 +33,6 @@
 except ImportError: # gae
     HAS_SEARCH_RESTRICTION = False
 
-
-def xhtml_wrap(self, source):
-    # XXX factor out, watch view.py ~ Maintemplate.doctype
-    if self.req.xhtml_browser():
-        dt = STRICT_DOCTYPE
-    else:
-        dt = STRICT_DOCTYPE_NOEXT
-    head = u'<?xml version="1.0"?>\n' + dt
-    return head + u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">%s</div>' % source.strip()
-
 def jsonize(func):
     """decorator to sets correct content_type and calls `simplejson.dumps` on
     results
@@ -58,7 +48,8 @@
     def wrapper(self, *args, **kwargs):
         self.req.set_content_type(self.req.html_content_type())
         result = func(self, *args, **kwargs)
-        return xhtml_wrap(self, result)
+        return ''.join((self.req.document_surrounding_div(), result.strip(),
+                        u'</div>'))
     wrapper.__name__ = func.__name__
     return wrapper
 
@@ -78,7 +69,7 @@
     id = 'login'
 
     def publish(self, rset=None):
-        """log in the application"""
+        """log in the instance"""
         if self.config['auth-mode'] == 'http':
             # HTTP authentication
             raise ExplicitLogin()
@@ -91,7 +82,7 @@
     id = 'logout'
 
     def publish(self, rset=None):
-        """logout from the application"""
+        """logout from the instance"""
         return self.appli.session_handler.logout(self.req)
 
 
@@ -128,12 +119,12 @@
                 req.set_message(req._("error while handling __method: %s") % req._(ex))
         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
         try:
-            view = self.vreg.select_view(vid, req, rset)
+            view = self.vreg.select('views', vid, req, rset=rset)
         except ObjectNotFound:
             self.warning("the view %s could not be found", vid)
             req.set_message(req._("The view %s could not be found") % vid)
             vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
+            view = self.vreg.select('views', vid, req, rset=rset)
         except NoSelectableObject:
             if rset:
                 req.set_message(req._("The view %s can not be applied to this query") % vid)
@@ -141,7 +132,7 @@
                 req.set_message(req._("You have no access to this view or it's not applyable to current data"))
             self.warning("the view %s can not be applied to this query", vid)
             vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
+            view = self.vreg.select('views', vid, req, rset=rset)
         return view, rset
 
     def add_to_breadcrumbs(self, view):
@@ -179,6 +170,7 @@
 
 
 def _validation_error(req, ex):
+    req.cnx.rollback()
     forminfo = req.get_session_data(req.form.get('__errorurl'), pop=True)
     foreid = ex.entity
     eidmap = req.data.get('eidmap', {})
@@ -191,20 +183,17 @@
 def _validate_form(req, vreg):
     # XXX should use the `RemoteCallFailed` mechanism
     try:
-        ctrl = vreg.select(vreg.registry_objects('controllers', 'edit'),
-                           req=req)
+        ctrl = vreg.select('controllers', 'edit', req=req)
     except NoSelectableObject:
         return (False, {None: req._('not authorized')})
     try:
         ctrl.publish(None)
     except ValidationError, ex:
-        req.cnx.rollback()
         return (False, _validation_error(req, ex))
     except Redirect, ex:
         try:
             req.cnx.commit() # ValidationError may be raise on commit
         except ValidationError, ex:
-            req.cnx.rollback()
             return (False, _validation_error(req, ex))
         else:
             return (True, ex.location)
@@ -218,18 +207,21 @@
 class FormValidatorController(Controller):
     id = 'validateform'
 
+    def response(self, domid, status, args):
+        self.req.set_content_type('text/html')
+        jsargs = simplejson.dumps( (status, args) )
+        return """<script type="text/javascript">
+ window.parent.handleFormValidationResponse('%s', null, null, %s);
+</script>""" %  (domid, jsargs)
+
     def publish(self, rset=None):
         self.req.json_request = True
         # XXX unclear why we have a separated controller here vs
         # js_validate_form on the json controller
         status, args = _validate_form(self.req, self.vreg)
-        self.req.set_content_type('text/html')
-        jsarg = simplejson.dumps( (status, args) )
         domid = self.req.form.get('__domid', 'entityForm').encode(
             self.req.encoding)
-        return """<script type="text/javascript">
- window.parent.handleFormValidationResponse('%s', null, null, %s);
-</script>""" %  (domid, simplejson.dumps( (status, args) ))
+        return self.response(domid, status, args)
 
 
 class JSonController(Controller):
@@ -263,6 +255,8 @@
         except RemoteCallFailed:
             raise
         except Exception, ex:
+            import traceback
+            traceback.print_exc()
             self.exception('an exception occured while calling js_%s(%s): %s',
                            fname, args, ex)
             raise RemoteCallFailed(repr(ex))
@@ -315,10 +309,10 @@
             rset = None
         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
         try:
-            view = self.vreg.select_view(vid, req, rset)
+            view = self.vreg.select('views', vid, req, rset=rset)
         except NoSelectableObject:
             vid = req.form.get('fallbackvid', 'noresult')
-            view = self.vreg.select_view(vid, req, rset)
+            view = self.vreg.select('views', vid, req, rset=rset)
         divid = req.form.get('divid', 'pageContent')
         # we need to call pagination before with the stream set
         stream = view.set_stream()
@@ -348,8 +342,7 @@
         entity = self.vreg.etype_class('CWProperty')(self.req, None, None)
         entity.eid = varname
         entity['pkey'] = propkey
-        form = self.vreg.select_object('forms', 'edition', self.req, None,
-                                       entity=entity)
+        form = self.vreg.select('forms', 'edition', self.req, entity=entity)
         form.form_build_context()
         vfield = form.field_by_name('value')
         renderer = FormRenderer(self.req)
@@ -362,7 +355,7 @@
             rset = self._exec(rql)
         else:
             rset = None
-        comp = self.vreg.select_object(registry, compid, self.req, rset)
+        comp = self.vreg.select(registry, compid, self.req, rset=rset)
         if extraargs is None:
             extraargs = {}
         else: # we receive unicode keys which is not supported by the **syntax
@@ -374,9 +367,8 @@
     @check_pageid
     @xhtmlize
     def js_inline_creation_form(self, peid, ttype, rtype, role):
-        view = self.vreg.select_view('inline-creation', self.req, None,
-                                     etype=ttype, peid=peid, rtype=rtype,
-                                     role=role)
+        view = self.vreg.select('views', 'inline-creation', self.req,
+                                etype=ttype, peid=peid, rtype=rtype, role=role)
         return view.render(etype=ttype, peid=peid, rtype=rtype, role=role)
 
     @jsonize
@@ -570,7 +562,8 @@
         self.smtp.sendmail(helo_addr, [recipient], msg.as_string())
 
     def publish(self, rset=None):
-        # XXX this allow anybody with access to an cubicweb application to use it as a mail relay
+        # XXX this allows users with access to an cubicweb instance to use it as
+        # a mail relay
         body = self.req.form['mailbody']
         subject = self.req.form['subject']
         for recipient in self.recipients():
--- a/web/views/basetemplates.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/basetemplates.py	Fri Jul 31 14:25:30 2009 +0200
@@ -15,6 +15,7 @@
 from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW
 from cubicweb.utils import make_uid, UStringIO
 
+
 # main templates ##############################################################
 
 class LogInOutTemplate(MainTemplate):
@@ -83,15 +84,15 @@
     def call(self, view):
         view.set_request_content_type()
         view.set_stream()
-        xhtml_wrap = (self.req.form.has_key('__notemplate') and view.templatable
-                      and view.content_type == self.req.html_content_type())
-        if xhtml_wrap:
-            view.w(u'<?xml version="1.0"?>\n' + self.doctype)
-            view.w(u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
-        # have to replace our unicode stream using view's binary stream
-        view.render()
-        if xhtml_wrap:
+        if (self.req.form.has_key('__notemplate') and view.templatable
+            and view.content_type == self.req.html_content_type()):
+            view.w(self.req.document_surrounding_div())
+            view.render()
             view.w(u'</div>')
+        else:
+            view.render()
+        # have to replace our stream by view's stream (which may be a binary
+        # stream)
         self._stream = view._stream
 
 
@@ -112,9 +113,9 @@
         if vtitle:
             w(u'<h1 class="vtitle">%s</h1>\n' % xml_escape(vtitle))
         # display entity type restriction component
-        etypefilter = self.vreg.select_component('etypenavigation',
-                                                 self.req, self.rset)
-        if etypefilter and etypefilter.propval('visible'):
+        etypefilter = self.vreg.select_vobject('components', 'etypenavigation',
+                                              self.req, rset=self.rset)
+        if etypefilter:
             etypefilter.render(w=w)
         self.nav_html = UStringIO()
         if view and view.need_navigation:
@@ -137,7 +138,8 @@
         w = self.whead
         lang = self.req.lang
         self.write_doctype()
-        w(u'<base href="%s" />' % xml_escape(self.req.base_url()))
+        # explictly close the <base> tag to avoid IE 6 bugs while browsing DOM
+        w(u'<base href="%s"></base>' % xml_escape(self.req.base_url()))
         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
           % (content_type, self.req.encoding))
         w(u'\n'.join(additional_headers) + u'\n')
@@ -152,10 +154,12 @@
         w(u'<div id="page"><table width="100%" border="0" id="mainLayout"><tr>\n')
         self.nav_column(view, 'left')
         w(u'<td id="contentcol">\n')
-        rqlcomp = self.vreg.select_component('rqlinput', self.req, self.rset)
+        rqlcomp = self.vreg.select_object('components', 'rqlinput', self.req,
+                                          rset=self.rset)
         if rqlcomp:
             rqlcomp.render(w=self.w, view=view)
-        msgcomp = self.vreg.select_component('applmessages', self.req, self.rset)
+        msgcomp = self.vreg.select_object('components', 'applmessages',
+                                          self.req, rset=self.rset)
         if msgcomp:
             msgcomp.render(w=self.w)
         self.content_header(view)
@@ -169,7 +173,7 @@
         self.w(u'</body>')
 
     def nav_column(self, view, context):
-        boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+        boxes = list(self.vreg.possible_vobjects('boxes', self.req, rset=self.rset,
                                                  view=view, context=context))
         if boxes:
             self.w(u'<td class="navcol"><div class="navboxes">\n')
@@ -196,7 +200,7 @@
         """display an unexpected error"""
         self.set_request_content_type()
         self.req.reset_headers()
-        view = self.vreg.select_view('error', self.req, self.rset)
+        view = self.vreg.select('views', 'error', self.req, rset=self.rset)
         self.template_header(self.content_type, view, self.req._('an error occured'),
                              [NOINDEX, NOFOLLOW])
         view.render(w=self.w)
@@ -238,7 +242,7 @@
         w(u'<table width="100%" height="100%" border="0"><tr>\n')
         w(u'<td class="navcol">\n')
         self.topleft_header()
-        boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+        boxes = list(self.vreg.possible_vobjects('boxes', self.req, rset=self.rset,
                                                  view=view, context='left'))
         if boxes:
             w(u'<div class="navboxes">\n')
@@ -253,11 +257,14 @@
             w(u'<h1 class="vtitle">%s</h1>' % xml_escape(vtitle))
 
     def topleft_header(self):
-        self.w(u'<table id="header"><tr>\n')
-        self.w(u'<td>')
-        self.vreg.select_component('logo', self.req, self.rset).render(w=self.w)
-        self.w(u'</td>\n')
-        self.w(u'</tr></table>\n')
+        logo = self.vreg.select_vobject('components', 'logo', self.req,
+                                        rset=self.rset)
+        if logo:
+            self.w(u'<table id="header"><tr>\n')
+            self.w(u'<td>')
+            logo.render(w=self.w)
+            self.w(u'</td>\n')
+            self.w(u'</tr></table>\n')
 
 # page parts templates ########################################################
 
@@ -292,11 +299,11 @@
             self.req.add_js(jscript, localfile=False)
 
     def alternates(self):
-        urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
+        urlgetter = self.vreg.select_object('components', 'rss_feed_url',
+                                            self.req, rset=self.rset)
         if urlgetter is not None:
-            url = urlgetter.feed_url()
             self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n'
-                       %  xml_escape(url))
+                       %  xml_escape(urlgetter.feed_url()))
 
     def pageid(self):
         req = self.req
@@ -322,24 +329,29 @@
         """build the top menu with authentification info and the rql box"""
         self.w(u'<table id="header"><tr>\n')
         self.w(u'<td id="firstcolumn">')
-        self.vreg.select_component('logo', self.req, self.rset).render(w=self.w)
+        logo = self.vreg.select_vobject('components', 'logo',
+                                        self.req, rset=self.rset)
+        if logo:
+            logo.render(w=self.w)
         self.w(u'</td>\n')
         # appliname and breadcrumbs
         self.w(u'<td id="headtext">')
-        comp = self.vreg.select_component('appliname', self.req, self.rset)
-        if comp and comp.propval('visible'):
-            comp.render(w=self.w)
-        comp = self.vreg.select_component('breadcrumbs', self.req, self.rset, view=view)
-        if comp and comp.propval('visible'):
-            comp.render(w=self.w, view=view)
+        for cid in ('appliname', 'breadcrumbs'):
+            comp = self.vreg.select_vobject('components', cid,
+                                            self.req, rset=self.rset)
+            if comp:
+                comp.render(w=self.w)
         self.w(u'</td>')
         # logged user and help
         self.w(u'<td>\n')
-        comp = self.vreg.select_component('loggeduserlink', self.req, self.rset)
-        comp.render(w=self.w)
+        comp = self.vreg.select_vobject('components', 'loggeduserlink',
+                                        self.req, rset=self.rset)
+        if comp:
+            comp.render(w=self.w)
         self.w(u'</td><td>')
-        helpcomp = self.vreg.select_component('help', self.req, self.rset)
-        if helpcomp: # may not be available if Card is not defined in the schema
+        helpcomp = self.vreg.select_vobject('components', 'help',
+                                            self.req, rset=self.rset)
+        if helpcomp:
             helpcomp.render(w=self.w)
         self.w(u'</td>')
         # lastcolumn
@@ -394,7 +406,7 @@
     def call(self, view, **kwargs):
         """by default, display informal messages in content header"""
         components = self.vreg.possible_vobjects('contentnavigation',
-                                                 self.req, self.rset,
+                                                 self.req, rset=self.rset,
                                                  view=view, context='navtop')
         if components:
             self.w(u'<div id="contentheader">')
@@ -411,7 +423,7 @@
 
     def call(self, view, **kwargs):
         components = self.vreg.possible_vobjects('contentnavigation',
-                                                 self.req, self.rset,
+                                                 self.req, rset=self.rset,
                                                  view=view, context='navbottom')
         if components:
             self.w(u'<div id="contentfooter">')
--- a/web/views/baseviews.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/baseviews.py	Fri Jul 31 14:25:30 2009 +0200
@@ -2,7 +2,7 @@
 
 * noresult, final
 * primary, sidebox
-* secondary, oneline, incontext, outofcontext, text
+* oneline, incontext, outofcontext, text
 * list
 
 
@@ -20,7 +20,8 @@
 from logilab.mtconverter import TransformError, xml_escape, xml_escape
 
 from cubicweb import NoSelectableObject
-from cubicweb.selectors import yes, empty_rset
+from cubicweb.selectors import yes, empty_rset, one_etype_rset
+from cubicweb.schema import display_name
 from cubicweb.view import EntityView, AnyRsetView, View
 from cubicweb.common.uilib import cut, printable_value
 
@@ -100,6 +101,7 @@
         self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime))
 
 
+# XXX deprecated
 class SecondaryView(EntityView):
     id = 'secondary'
     title = _('secondary')
@@ -179,9 +181,6 @@
         self.w(u'</div>')
 
 
-# new default views for finner control in general views , to use instead of
-# oneline / secondary
-
 class InContextTextView(TextView):
     id = 'textincontext'
     title = None # not listed as a possible view
@@ -236,6 +235,7 @@
 
         :param listid: the DOM id to use for the root element
         """
+        # XXX much of the behaviour here should probably be outside this view
         if subvid is None and 'subvid' in self.req.form:
             subvid = self.req.form.pop('subvid') # consume it
         if listid:
@@ -258,28 +258,6 @@
         self.wview(self.item_vid, self.rset, row=row, col=col, vid=vid, **kwargs)
         self.w(u'</li>\n')
 
-    def url(self):
-        """overrides url method so that by default, the view list is called
-        with sorted entities
-        """
-        coltypes = self.rset.column_types(0)
-        # don't want to generate the rql if there is some restriction on
-        # something else than the entity type
-        if len(coltypes) == 1:
-            # XXX norestriction is not correct here. For instance, in cases like
-            # Any P,N WHERE P is Project, P name N
-            # norestriction should equal True
-            restr = self.rset.syntax_tree().children[0].where
-            norestriction = (isinstance(restr, nodes.Relation) and
-                             restr.is_types_restriction())
-            if norestriction:
-                etype = iter(coltypes).next()
-                return self.build_url(etype.lower(), vid=self.id)
-        if len(self.rset) == 1:
-            entity = self.rset.get_entity(0, 0)
-            return self.build_url(entity.rest_path(), vid=self.id)
-        return self.build_url(rql=self.rset.printable_rql(), vid=self.id)
-
 
 class ListItemView(EntityView):
     id = 'listitem'
@@ -307,6 +285,31 @@
     redirect_vid = 'incontext'
 
 
+class AdaptedListView(EntityView):
+    """list of entities of the same type"""
+    id = 'adaptedlist'
+    __select__ = one_etype_rset()
+    item_vid = 'adaptedlistitem'
+
+    @property
+    def title(self):
+        etype = iter(self.rset.column_types(0)).next()
+        return display_name(self.req, etype, form='plural')
+
+    def call(self, **kwargs):
+        """display a list of entities by calling their <item_vid> view"""
+        if not 'vtitle' in self.req.form:
+            self.w(u'<h1>%s</h1>' % self.title)
+        super(AdaptedListView, self).call(**kwargs)
+
+    def cell_call(self, row, col=0, vid=None, **kwargs):
+        self.wview(self.item_vid, self.rset, row=row, col=col, vid=vid, **kwargs)
+
+
+class AdaptedListItemView(ListItemView):
+    id = 'adaptedlistitem'
+
+
 class CSVView(SimpleListView):
     id = 'csv'
     redirect_vid = 'incontext'
--- a/web/views/bookmark.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/bookmark.py	Fri Jul 31 14:25:30 2009 +0200
@@ -85,7 +85,7 @@
         if eschema.has_perm(req, 'add') and rschema.has_perm(req, 'add', toeid=ueid):
             boxmenu = BoxMenu(req._('manage bookmarks'))
             linkto = 'bookmarked_by:%s:subject' % ueid
-            # use a relative path so that we can move the application without
+            # use a relative path so that we can move the instance without
             # loosing bookmarks
             path = req.relative_path()
             url = self.create_url(self.etype, __linkto=linkto, path=path)
--- a/web/views/boxes.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/boxes.py	Fri Jul 31 14:25:30 2009 +0200
@@ -144,20 +144,16 @@
         if 'in_state' in entity.e_schema.subject_relations() and entity.in_state:
             _ = self.req._
             state = entity.in_state[0]
-            transitions = list(state.transitions(entity))
-            if transitions:
-                menu_title = u'%s: %s' % (_('state'), state.view('text'))
-                menu_items = []
-                for tr in transitions:
-                    url = entity.absolute_url(vid='statuschange', treid=tr.eid)
-                    menu_items.append(self.mk_action(_(tr.name), url))
-                box.append(BoxMenu(menu_title, menu_items))
-            # when there are no possible transition, put state if the menu if
-            # there are some other actions
-            elif not box.is_empty():
-                menu_title = u'<a title="%s">%s: <i>%s</i></a>' % (
-                    _('no possible transition'), _('state'), state.view('text'))
-                box.append(RawBoxItem(menu_title, 'boxMainactions'))
+            menu_title = u'%s: %s' % (_('state'), state.view('text'))
+            menu_items = []
+            for tr in state.transitions(entity):
+                url = entity.absolute_url(vid='statuschange', treid=tr.eid)
+                menu_items.append(self.mk_action(_(tr.name), url))
+            wfurl = self.build_url('cwetype/%s'%entity.e_schema, vid='workflow')
+            menu_items.append(self.mk_action(_('view workflow'), wfurl))
+            wfurl = entity.absolute_url(vid='wfhistory')
+            menu_items.append(self.mk_action(_('view history'), wfurl))
+            box.append(BoxMenu(menu_title, menu_items))
         return None
 
     def linkto_url(self, entity, rtype, etype, target):
--- a/web/views/cwproperties.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/cwproperties.py	Fri Jul 31 14:25:30 2009 +0200
@@ -64,6 +64,7 @@
 
 
 class SystemCWPropertiesForm(FormViewMixIn, StartupView):
+    """site-wide properties edition form"""
     id = 'systempropertiesform'
     __select__ = none_rset() & match_user_groups('managers')
 
@@ -94,7 +95,6 @@
         return status
 
     def call(self, **kwargs):
-        """The default view representing the application's index"""
         self.req.add_js(('cubicweb.edition.js', 'cubicweb.preferences.js', 'cubicweb.ajax.js'))
         self.req.add_css('cubicweb.preferences.css')
         vreg = self.vreg
@@ -188,11 +188,11 @@
 
     def form(self, formid, keys, splitlabel=False):
         buttons = [SubmitButton()]
-        form = self.vreg.select_object('forms', 'composite', self.req,
-                                  domid=formid, action=self.build_url(),
-                                  form_buttons=buttons,
-                                  onsubmit="return validatePrefsForm('%s')" % formid,
-                                  submitmsg=self.req._('changes applied'))
+        form = self.vreg.select('forms', 'composite', self.req,
+                                domid=formid, action=self.build_url(),
+                                form_buttons=buttons,
+                                onsubmit="return validatePrefsForm('%s')" % formid,
+                                submitmsg=self.req._('changes applied'))
         path = self.req.relative_path()
         if '?' in path:
             path, params = path.split('?', 1)
@@ -200,8 +200,8 @@
         form.form_add_hidden('__redirectpath', path)
         for key in keys:
             self.form_row(form, key, splitlabel)
-        renderer = self.vreg.select_object('formrenderers', 'cwproperties', self.req,
-                                           display_progress_div=False)
+        renderer = self.vreg.select('formrenderers', 'cwproperties', self.req,
+                                    display_progress_div=False)
         return form.form_render(renderer=renderer)
 
     def form_row(self, form, key, splitlabel):
@@ -210,8 +210,8 @@
             label = key.split('.')[-1]
         else:
             label = key
-        subform = self.vreg.select_object('forms', 'base', self.req, entity=entity,
-                                     mainform=False)
+        subform = self.vreg.select('forms', 'base', self.req, entity=entity,
+                                   mainform=False)
         subform.append_field(PropertyValueField(name='value', label=label,
                                                 eidparam=True))
         subform.vreg = self.vreg
@@ -226,6 +226,7 @@
 
 
 class CWPropertiesForm(SystemCWPropertiesForm):
+    """user's preferences properties edition form"""
     id = 'propertiesform'
     __select__ = (
         (none_rset() & match_user_groups('users','managers'))
@@ -259,7 +260,7 @@
 
 class PlaceHolderWidget(object):
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         domid = form.context[field]['id']
         # empty span as well else html validation fail (label is refering to
         # this id)
@@ -272,7 +273,7 @@
         self.value = value
         self.msg = msg
 
-    def render(self, form, field):
+    def render(self, form, field, renderer):
         domid = form.context[field]['id']
         value = '<span class="value" id="%s">%s</span>' % (domid, self.value)
         if self.msg:
@@ -291,7 +292,7 @@
         wdg.attrs['tabindex'] = form.req.next_tabindex()
         wdg.attrs['onchange'] = "javascript:setPropValueWidget('%s', %s)" % (
             form.edited_entity.eid, form.req.next_tabindex())
-        return wdg.render(form, self)
+        return wdg.render(form, self, renderer)
 
     def vocabulary(self, form):
         entity = form.edited_entity
@@ -312,7 +313,7 @@
         wdg = self.get_widget(form)
         if tabindex is not None:
             wdg.attrs['tabindex'] = tabindex
-        return wdg.render(form, self)
+        return wdg.render(form, self, renderer)
 
     def form_init(self, form):
         entity = form.edited_entity
--- a/web/views/editforms.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/editforms.py	Fri Jul 31 14:25:30 2009 +0200
@@ -43,7 +43,31 @@
         js, nodeid, label)
 
 
-class DeleteConfForm(FormViewMixIn, EntityView):
+class DeleteConfForm(forms.CompositeForm):
+    id = 'deleteconf'
+    __select__ = non_final_entity()
+
+    domid = 'deleteconf'
+    copy_nav_params = True
+    form_buttons = [Button(stdmsgs.YES, cwaction='delete'),
+                    Button(stdmsgs.NO, cwaction='cancel')]
+    @property
+    def action(self):
+        return self.build_url('edit')
+
+    def __init__(self, *args, **kwargs):
+        super(DeleteConfForm, self).__init__(*args, **kwargs)
+        done = set()
+        for entity in self.rset.entities():
+            if entity.eid in done:
+                continue
+            done.add(entity.eid)
+            subform = self.vreg.select('forms', 'base', self.req, entity=entity,
+                                       mainform=False)
+            self.form_add_subform(subform)
+
+
+class DeleteConfFormView(FormViewMixIn, EntityView):
     """form used to confirm deletion of some entities"""
     id = 'deleteconf'
     title = _('delete')
@@ -59,20 +83,10 @@
           % _('this action is not reversible!'))
         # XXX above message should have style of a warning
         w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s) ?'))
-        form = self.vreg.select_object('forms', 'composite', req, domid='deleteconf',
-                                       copy_nav_params=True,
-                                       action=self.build_url('edit'), onsubmit=onsubmit,
-                                       form_buttons=[Button(stdmsgs.YES, cwaction='delete'),
-                                                     Button(stdmsgs.NO, cwaction='cancel')])
-        done = set()
+        form = self.vreg.select('forms', self.id, req, rset=self.rset,
+                                onsubmit=onsubmit)
         w(u'<ul>\n')
         for entity in self.rset.entities():
-            if entity.eid in done:
-                continue
-            done.add(entity.eid)
-            subform = self.vreg.select_object('forms', 'base', req, entity=entity,
-                                              mainform=False)
-            form.form_add_subform(subform)
             # don't use outofcontext view or any other that may contain inline edition form
             w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
                                       href=entity.absolute_url()))
@@ -89,8 +103,14 @@
     __select__ = non_final_entity() & match_kwargs('rtype')
     # FIXME editableField class could be toggleable from userprefs
 
+    # add metadata to allow edition of metadata attributes (not considered by
+    # edition form by default)
+    attrcategories = ('primary', 'secondary', 'metadata')
+
     _onclick = u"showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
-    _defaultlandingzone = u'<img title="%(msg)s" src="data/file.gif" alt="%(msg)s"/>'
+    _defaultlandingzone = (u'<img title="%(msg)s" '
+                           'src="data/accessories-text-editor.png" '
+                           'alt="%(msg)s"/>')
     _landingzonemsg = _('click to edit this field')
     # default relation vids according to cardinality
     _one_rvid = 'incontext'
@@ -131,10 +151,14 @@
         lzone = self._build_landing_zone(landing_zone)
         # compute value, checking perms, build form
         if rschema.is_final():
-            value = entity.printable_value(rtype) or default
-            if not entity.has_perm('update'):
+            value = entity.printable_value(rtype)
+            etype = str(entity.e_schema)
+            ttype = rschema.targets(etype, role)[0]
+            afs = uicfg.autoform_section.etype_get(etype, rtype, role, ttype)
+            if not (afs in self.attrcategories and entity.has_perm('update')):
                 self.w(value)
                 return
+            value = value or default
             self._attribute_form(entity, value, rtype, role, reload,
                                  row, col, default, lzone)
         else:
@@ -147,8 +171,11 @@
             if rvid is None:
                 rvid = self._compute_best_vid(entity, rtype, role)
             rset = entity.related(rtype, role)
-            candidate = self.view(rvid, rset, 'null')
-            value = candidate or default
+            if rset:
+                value = self.view(rvid, rset)
+            else:
+                value = default
+            # XXX check autoform_section. what if 'generic'?
             if role == 'subject' and not rschema.has_perm(self.req, 'add',
                                                           fromeid=entity.eid):
                 return self.w(value)
@@ -180,12 +207,12 @@
                     % event_data)
         cancelclick = "hideInlineEdit(%s,\'%s\',\'%s\')" % (
             entity.eid, rtype, divid)
-        form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
-                                       domid='%s-form' % divid, cssstyle='display: none',
-                                       onsubmit=onsubmit, action='#',
-                                       form_buttons=[SubmitButton(),
-                                                     Button(stdmsgs.BUTTON_CANCEL,
-                                                            onclick=cancelclick)])
+        form = self.vreg.select('forms', 'base', self.req, entity=entity,
+                                domid='%s-form' % divid, cssstyle='display: none',
+                                onsubmit=onsubmit, action='#',
+                                form_buttons=[SubmitButton(),
+                                              Button(stdmsgs.BUTTON_CANCEL,
+                                                     onclick=cancelclick)])
         field = guess_field(entity.e_schema, entity.schema.rschema(rtype), role)
         form.append_field(field)
         w = self.w
@@ -216,11 +243,12 @@
                    Button(stdmsgs.BUTTON_CANCEL,
                           onclick="hideInlineEdit(%s,\'%s\',\'%s\')" % (
                               eid, rtype, divid))]
-        form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
-                                       row=row, col=col, form_buttons=buttons,
-                                       domid='%s-form' % divid, action='#',
-                                       cssstyle='display: none',
-                                       onsubmit=onsubmit % event_data)
+        form = self.vreg.select('forms', 'edition', self.req, rset=self.rset,
+                                row=row, col=col, form_buttons=buttons,
+                                attrcategories=self.attrcategories,
+                                domid='%s-form' % divid, action='#',
+                                cssstyle='display: none',
+                                onsubmit=onsubmit % event_data)
         w = self.w
         w(u'<div class="field">')
         w(u'<div id="%s" style="display: inline">' % divid)
@@ -250,9 +278,9 @@
     def render_form(self, entity):
         """fetch and render the form"""
         self.form_title(entity)
-        form = self.vreg.select_object('forms', 'edition', self.req, entity.rset,
-                                       row=entity.row, col=entity.col, entity=entity,
-                                       submitmsg=self.submited_message())
+        form = self.vreg.select('forms', 'edition', self.req, rset=entity.rset,
+                                row=entity.row, col=entity.col, entity=entity,
+                                submitmsg=self.submited_message())
         self.init_form(form, entity)
         self.w(form.form_render(formvid=u'edition'))
 
@@ -369,9 +397,9 @@
         kwargs.setdefault('__redirectrql', rset.printable_rql())
         super(TableEditForm, self).__init__(req, rset, **kwargs)
         for row in xrange(len(self.rset)):
-            form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
-                                           row=row, attrcategories=('primary',),
-                                           mainform=False)
+            form = self.vreg.select('forms', 'edition', self.req, rset=self.rset,
+                                    row=row, attrcategories=('primary',),
+                                    mainform=False)
             # XXX rely on the EntityCompositeFormRenderer to put the eid input
             form.remove_field(form.field_by_name('eid'))
             self.form_add_subform(form)
@@ -387,7 +415,7 @@
         should be the eid
         """
         #self.form_title(entity)
-        form = self.vreg.select_object('forms', self.id, self.req, self.rset)
+        form = self.vreg.select('forms', self.id, self.req, rset=self.rset)
         self.w(form.form_render())
 
 
@@ -418,9 +446,9 @@
 
     def render_form(self, entity, peid, rtype, role, **kwargs):
         """fetch and render the form"""
-        form = self.vreg.select_object('forms', 'edition', self.req, None,
-                                       entity=entity, form_renderer_id='inline',
-                                       mainform=False, copy_nav_params=False)
+        form = self.vreg.select('forms', 'edition', self.req, entity=entity,
+                                form_renderer_id='inline', mainform=False,
+                                copy_nav_params=False)
         self.add_hiddens(form, entity, peid, rtype, role)
         divid = '%s-%s-%s' % (peid, rtype, entity.eid)
         title = self.schema.rschema(rtype).display_name(self.req, role)
--- a/web/views/editviews.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/editviews.py	Fri Jul 31 14:25:30 2009 +0200
@@ -120,8 +120,7 @@
         eid = entity.eid
         pending_inserts = self.req.get_pending_inserts(eid)
         rtype = rschema.type
-        form = self.vreg.select_object('forms', 'edition', self.req,
-                                       self.rset, entity=entity)
+        form = self.vreg.select('forms', 'edition', self.req, entity=entity)
         field = form.field_by_name(rschema, target, entity.e_schema)
         limit = self.req.property_value('navigation.combobox-limit')
         for eview, reid in form.form_field_vocabulary(field, limit):
--- a/web/views/facets.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/facets.py	Fri Jul 31 14:25:30 2009 +0200
@@ -118,7 +118,7 @@
             self.w(self.bk_linkbox_template % bk_link)
 
     def get_facets(self, rset, mainvar):
-        return self.vreg.possible_vobjects('facets', self.req, rset,
+        return self.vreg.possible_vobjects('facets', self.req, rset=rset,
                                            context='facetbox',
                                            filtered_variable=mainvar)
 
--- a/web/views/formrenderers.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/formrenderers.py	Fri Jul 31 14:25:30 2009 +0200
@@ -85,6 +85,8 @@
         return '\n'.join(data)
 
     def render_label(self, form, field):
+        if field.label is None:
+            return u''
         label = self.req._(field.label)
         attrs = {'for': form.context[field]['id']}
         if field.required:
@@ -188,22 +190,40 @@
         return fields
 
     def _render_fields(self, fields, w, form):
-        w(u'<table class="%s">' % self.table_class)
+        byfieldset = {}
         for field in fields:
-            w(u'<tr>')
-            if self.display_label:
-                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
-            error = form.form_field_error(field)
-            if error:
-                w(u'<td class="error">')
-                w(error)
-            else:
-                w(u'<td>')
-            w(field.render(form, self))
-            if self.display_help:
-                w(self.render_help(form, field))
-            w(u'</td></tr>')
-        w(u'</table>')
+            byfieldset.setdefault(field.fieldset, []).append(field)
+        if form.fieldsets_in_order:
+            fieldsets = form.fieldsets_in_order
+        else:
+            fieldsets = byfieldset.keys()
+        for fieldset in fieldsets:
+            try:
+                fields = byfieldset.pop(fieldset)
+            except KeyError:
+                self.warning('no such fieldset: %s (%s)', fieldset, form)
+                continue
+            w(u'<fieldset class="%s">' % (fieldset or u'default'))
+            if fieldset:
+                w(u'<legend>%s</legend>' % self.req._(fieldset))
+            w(u'<table class="%s">' % self.table_class)
+            for field in fields:
+                w(u'<tr class="%s_%s_row">' % (field.name, field.role))
+                if self.display_label:
+                    w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
+                error = form.form_field_error(field)
+                if error:
+                    w(u'<td class="error">')
+                    w(error)
+                else:
+                    w(u'<td>')
+                w(field.render(form, self))
+                if self.display_help:
+                    w(self.render_help(form, field))
+                w(u'</td></tr>')
+            w(u'</table></fieldset>')
+        if byfieldset:
+            self.warning('unused fieldsets: %s', ', '.join(byfieldset))
 
     def render_buttons(self, w, form):
         if not form.form_buttons:
@@ -286,22 +306,42 @@
     """specific renderer for multiple entities edition form (muledit)"""
     id = 'composite'
 
+    _main_display_fields = None
+
     def render_fields(self, w, form, values):
         if not form.is_subform:
             w(u'<table class="listing">')
+            subfields = [field for field in form.forms[0].fields
+                         if self.display_field(form, field)
+                         and field.is_visible()]
+            if subfields:
+                # main form, display table headers
+                w(u'<tr class="header">')
+                w(u'<th align="left">%s</th>' %
+                  tags.input(type='checkbox',
+                             title=self.req._('toggle check boxes'),
+                             onclick="setCheckboxesState('eid', this.checked)"))
+                for field in subfields:
+                    w(u'<th>%s</th>' % self.req._(field.label))
+                w(u'</tr>')
         super(EntityCompositeFormRenderer, self).render_fields(w, form, values)
         if not form.is_subform:
             w(u'</table>')
+            if self._main_display_fields:
+                super(EntityCompositeFormRenderer, self)._render_fields(
+                    self._main_display_fields, w, form)
 
     def _render_fields(self, fields, w, form):
         if form.is_subform:
             entity = form.edited_entity
             values = form.form_previous_values
             qeid = eid_param('eid', entity.eid)
-            cbsetstate = "setCheckboxesState2('eid', %s, 'checked')" % xml_escape(dumps(entity.eid))
+            cbsetstate = "setCheckboxesState2('eid', %s, 'checked')" % \
+                         xml_escape(dumps(entity.eid))
             w(u'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
             # XXX turn this into a widget used on the eid field
-            w(u'<td>%s</td>' % checkbox('eid', entity.eid, checked=qeid in values))
+            w(u'<td>%s</td>' % checkbox('eid', entity.eid,
+                                        checked=qeid in values))
             for field in fields:
                 error = form.form_field_error(field)
                 if error:
@@ -309,22 +349,17 @@
                     w(error)
                 else:
                     w(u'<td>')
-                if isinstance(field.widget, (fwdgs.Select, fwdgs.CheckBox, fwdgs.Radio)):
+                if isinstance(field.widget, (fwdgs.Select, fwdgs.CheckBox,
+                                             fwdgs.Radio)):
                     field.widget.attrs['onchange'] = cbsetstate
                 elif isinstance(field.widget, fwdgs.Input):
                     field.widget.attrs['onkeypress'] = cbsetstate
+                # XXX else
                 w(u'<div>%s</div>' % field.render(form, self))
-                w(u'</td>')
+                w(u'</td>\n')
+            w(u'</tr>')
         else:
-            # main form, display table headers
-            w(u'<tr class="header">')
-            w(u'<th align="left">%s</th>'
-              % tags.input(type='checkbox', title=self.req._('toggle check boxes'),
-                           onclick="setCheckboxesState('eid', this.checked)"))
-            for field in self.forms[0].fields:
-                if self.display_field(form, field) and field.is_visible():
-                    w(u'<th>%s</th>' % self.req._(field.label))
-        w(u'</tr>')
+            self._main_display_fields = fields
 
 
 class EntityFormRenderer(EntityBaseFormRenderer):
@@ -435,8 +470,6 @@
         """create a form to edit entity's inlined relations"""
         if not hasattr(form, 'inlined_relations'):
             return
-        entity = form.edited_entity
-        __ = self.req.__
         for rschema, targettypes, role in form.inlined_relations():
             # show inline forms only if there's one possible target type
             # for rschema
@@ -447,37 +480,42 @@
                 continue
             targettype = targettypes[0].type
             if form.should_inline_relation_form(rschema, targettype, role):
-                w(u'<div id="inline%sslot">' % rschema)
-                existant = entity.has_eid() and entity.related(rschema)
-                if existant:
-                    # display inline-edition view for all existing related entities
-                    w(form.view('inline-edition', existant, rtype=rschema, role=role,
-                                ptype=entity.e_schema, peid=entity.eid))
-                if role == 'subject':
-                    card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
-                else:
-                    card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
-                # there is no related entity and we need at least one: we need to
-                # display one explicit inline-creation view
-                if form.should_display_inline_creation_form(rschema, existant, card):
-                    w(form.view('inline-creation', None, etype=targettype,
-                                peid=entity.eid, ptype=entity.e_schema,
-                                rtype=rschema, role=role))
-                # we can create more than one related entity, we thus display a link
-                # to add new related entities
-                if form.should_display_add_new_relation_link(rschema, existant, card):
-                    divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
-                    w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
-                      % divid)
-                    js = "addInlineCreationForm('%s', '%s', '%s', '%s')" % (
-                        entity.eid, targettype, rschema, role)
-                    if card in '1?':
-                        js = "toggleVisibility('%s'); %s" % (divid, js)
-                    w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
-                      % (rschema, entity.eid, js, __('add a %s' % targettype)))
-                    w(u'</div>')
-                    w(u'<div class="trame_grise">&nbsp;</div>')
-                w(u'</div>')
+                self.inline_relation_form(w, form, rschema, targettype, role)
+
+    def inline_relation_form(self, w, form, rschema, targettype, role):
+        entity = form.edited_entity
+        __ = self.req.__
+        w(u'<div id="inline%sslot">' % rschema)
+        existant = entity.has_eid() and entity.related(rschema)
+        if existant:
+            # display inline-edition view for all existing related entities
+            w(form.view('inline-edition', existant, rtype=rschema, role=role,
+                        ptype=entity.e_schema, peid=entity.eid))
+        if role == 'subject':
+            card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
+        else:
+            card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
+        # there is no related entity and we need at least one: we need to
+        # display one explicit inline-creation view
+        if form.should_display_inline_creation_form(rschema, existant, card):
+            w(form.view('inline-creation', None, etype=targettype,
+                        peid=entity.eid, ptype=entity.e_schema,
+                        rtype=rschema, role=role))
+        # we can create more than one related entity, we thus display a link
+        # to add new related entities
+        if form.should_display_add_new_relation_link(rschema, existant, card):
+            divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
+            w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
+              % divid)
+            js = "addInlineCreationForm('%s', '%s', '%s', '%s')" % (
+                entity.eid, targettype, rschema, role)
+            if card in '1?':
+                js = "toggleVisibility('%s'); %s" % (divid, js)
+            w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
+              % (rschema, entity.eid, js, __('add a %s' % targettype)))
+            w(u'</div>')
+            w(u'<div class="trame_grise">&nbsp;</div>')
+        w(u'</div>')
 
 
 class EntityInlinedFormRenderer(EntityFormRenderer):
--- a/web/views/forms.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/forms.py	Fri Jul 31 14:25:30 2009 +0200
@@ -20,16 +20,49 @@
 
 
 class FieldsForm(form.Form):
+    """base class for fields based forms.
+
+    The following attributes may be either set on subclasses or given on
+    form selection to customize the generated form:
+
+    * `needs_js`: sequence of javascript files that should be added to handle
+      this form (through `req.add_js`)
+
+    * `needs_css`: sequence of css files that should be added to handle this
+      form (through `req.add_css`)
+
+    * `domid`: value for the "id" attribute of the <form> tag
+
+    * `action`: value for the "action" attribute of the <form> tag
+
+    * `onsubmit`: value for the "onsubmit" attribute of the <form> tag
+
+    * `cssclass`: value for the "class" attribute of the <form> tag
+
+    * `cssstyle`: value for the "style" attribute of the <form> tag
+
+    * `cwtarget`: value for the "cubicweb:target" attribute of the <form> tag
+
+    * `redirect_path`: relative to redirect to after submitting the form
+
+    * `copy_nav_params`: flag telling if navigation paramenters should be copied
+      back in hidden input
+
+    * `form_buttons`:  form buttons sequence (button widgets instances)
+
+    * `form_renderer_id`: id of the form renderer to use to render the form
+
+    * `fieldsets_in_order`: fieldset name sequence, to control order
+    """
     id = 'base'
 
     is_subform = False
+    internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
 
-    # attributes overrideable through __init__
-    internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
+    # attributes overrideable by subclasses or through __init__
     needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',)
     needs_css = ('cubicweb.form.css',)
     domid = 'form'
-    title = None
     action = None
     onsubmit = "return freezeFormButtons('%(domid)s');"
     cssclass = None
@@ -37,8 +70,9 @@
     cwtarget = None
     redirect_path = None
     copy_nav_params = False
-    form_buttons = None # form buttons (button widgets instances)
+    form_buttons = None
     form_renderer_id = 'default'
+    fieldsets_in_order = None
 
     def __init__(self, req, rset=None, row=None, col=None,
                  submitmsg=None, mainform=True,
@@ -145,9 +179,9 @@
         return renderer.render(self, values)
 
     def form_default_renderer(self):
-        return self.vreg.select_object('formrenderers', self.form_renderer_id,
-                                       self.req, self.rset,
-                                       row=self.row, col=self.col)
+        return self.vreg.select('formrenderers', self.form_renderer_id,
+                                self.req, rset=self.rset,
+                                row=self.row, col=self.col)
 
     def form_build_context(self, rendervalues=None):
         """build form context values (the .context attribute which is a
@@ -332,10 +366,9 @@
         return value
 
     def form_default_renderer(self):
-        return self.vreg.select_object('formrenderers', self.form_renderer_id,
-                                       self.req, self.rset,
-                                       row=self.row, col=self.col,
-                                       entity=self.edited_entity)
+        return self.vreg.select('formrenderers', self.form_renderer_id,
+                                self.req, rset=self.rset, row=self.row,
+                                col=self.col, entity=self.edited_entity)
 
     def form_build_context(self, values=None):
         """overriden to add edit[s|o] hidden fields and to ensure schema fields
--- a/web/views/idownloadable.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/idownloadable.py	Fri Jul 31 14:25:30 2009 +0200
@@ -122,7 +122,7 @@
     __select__ = implements(IDownloadable)
 
     def cell_call(self, row, col, title=None, **kwargs):
-        """the secondary view is a link to download the file"""
+        """the oneline view is a link to download the file"""
         entity = self.entity(row, col)
         url = xml_escape(entity.absolute_url())
         name = xml_escape(title or entity.download_file_name())
@@ -144,9 +144,21 @@
             self.wview(self.id, rset, row=i, col=0)
             self.w(u'</div>')
 
-    def cell_call(self, row, col):
+    def cell_call(self, row, col, width=None, height=None, link=False):
         entity = self.entity(row, col)
         #if entity.data_format.startswith('image/'):
-        self.w(u'<img src="%s" alt="%s"/>' % (xml_escape(entity.download_url()),
-                                              xml_escape(entity.download_file_name())))
+        imgtag = u'<img src="%s" alt="%s" ' % (xml_escape(entity.download_url()),
+                                               xml_escape(entity.download_file_name()))
+        if width:
+            imgtag += u'width="%i" ' % width
+        if height:
+            imgtag += u'height="%i" ' % height
+        imgtag += u'/>'
+        if link:
+            self.w(u'<a href="%s" alt="%s">%s</a>' % (entity.absolute_url(vid='download'),
+                                                      self.req._('download image'),
+                                                      imgtag))
+        else:
+            self.w(imgtag)
 
+
--- a/web/views/iprogress.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/iprogress.py	Fri Jul 31 14:25:30 2009 +0200
@@ -5,8 +5,8 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import xml_escape
 
@@ -168,7 +168,8 @@
     id = 'ic_progress_table_view'
 
     def call(self):
-        view = self.vreg.select_view('progress_table_view', self.req, self.rset)
+        view = self.vreg.select('views', 'progress_table_view', self.req,
+                                rset=self.rset)
         columns = list(view.columns)
         try:
             columns.remove('project')
--- a/web/views/magicsearch.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/magicsearch.py	Fri Jul 31 14:25:30 2009 +0200
@@ -42,7 +42,7 @@
     :param translations: the reverted l10n dict
 
     :type schema: `cubicweb.schema.Schema`
-    :param schema: the application's schema
+    :param schema: the instance's schema
     """
     # var_types is used as a map : var_name / var_type
     vartypes = {}
@@ -94,7 +94,7 @@
     :param ambiguous_nodes: a map : relation_node / (var_name, available_translations)
 
     :type schema: `cubicweb.schema.Schema`
-    :param schema: the application's schema
+    :param schema: the instance's schema
     """
     # Now, try to resolve ambiguous translations
     for relation, (var_name, translations_found) in ambiguous_nodes.items():
@@ -170,10 +170,7 @@
     """
     priority = 2
     def preprocess_query(self, uquery, req):
-        try:
-            rqlst = parse(uquery, print_errors=False)
-        except (RQLSyntaxError, BadRQLQuery), err:
-            return uquery,
+        rqlst = parse(uquery, print_errors=False)
         schema = self.vreg.schema
         # rql syntax tree will be modified in place if necessary
         translate_rql_tree(rqlst, trmap(self.config, schema, req.lang), schema)
@@ -204,7 +201,7 @@
             elif len(words) == 3:
                 args = self._three_words_query(*words)
             else:
-                args = self._multiple_words_query(words)
+                raise
         return args
 
     def _get_entity_type(self, word):
@@ -291,11 +288,6 @@
                                   self._complete_rql(word3, etype, searchattr=rtype))
         return rql, {'text': word3}
 
-    def _multiple_words_query(self, words):
-        """specific process for more than 3 words query"""
-        return ' '.join(words),
-
-
     def _expand_shortcut(self, etype, rtype, searchstr):
         """Expands shortcut queries on a non final relation to use has_text or
         the main attribute (according to possible entity type) if '%' is used in the
--- a/web/views/management.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/management.py	Fri Jul 31 14:25:30 2009 +0200
@@ -107,13 +107,12 @@
     def owned_by_edit_form(self, entity):
         self.w('<h3>%s</h3>' % self.req._('ownership'))
         msg = self.req._('ownerships have been changed')
-        form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
-                                       form_renderer_id='base',
-                                  submitmsg=msg,
-                                  form_buttons=[wdgs.SubmitButton()],
-                                  domid='ownership%s' % entity.eid,
-                                  __redirectvid='security',
-                                  __redirectpath=entity.rest_path())
+        form = self.vreg.select('forms', 'base', self.req, entity=entity,
+                                form_renderer_id='base', submitmsg=msg,
+                                form_buttons=[wdgs.SubmitButton()],
+                                domid='ownership%s' % entity.eid,
+                                __redirectvid='security',
+                                __redirectpath=entity.rest_path())
         field = guess_field(entity.e_schema, self.schema.rschema('owned_by'))
         form.append_field(field)
         self.w(form.form_render(display_progress_div=False))
@@ -161,16 +160,14 @@
             self.w(self.req._('no associated permissions'))
 
     def require_permission_edit_form(self, entity):
-        w = self.w
-        _ = self.req._
         newperm = self.vreg.etype_class('CWPermission')(self.req, None)
         newperm.eid = self.req.varmaker.next()
-        w(u'<p>%s</p>' % _('add a new permission'))
-        form = self.vreg.select_object('forms', 'base', self.req, entity=newperm,
-                                       form_buttons=[wdgs.SubmitButton()],
-                                       domid='reqperm%s' % entity.eid,
-                                       __redirectvid='security',
-                                       __redirectpath=entity.rest_path())
+        self.w(u'<p>%s</p>' % self.req._('add a new permission'))
+        form = self.vreg.select('forms', 'base', self.req, entity=newperm,
+                                form_buttons=[wdgs.SubmitButton()],
+                                domid='reqperm%s' % entity.eid,
+                                __redirectvid='security',
+                                __redirectpath=entity.rest_path())
         form.form_add_hidden('require_permission', entity.eid, role='object',
                              eidparam=True)
         permnames = getattr(entity, '__permissions__', None)
@@ -186,8 +183,8 @@
         form.append_field(field)
         field = guess_field(cwpermschema, self.schema.rschema('require_group'))
         form.append_field(field)
-        renderer = self.vreg.select_object('formrenderers', 'htable', self.req,
-                                           rset=None, display_progress_div=False)
+        renderer = self.vreg.select('formrenderers', 'htable', self.req,
+                                    rset=None, display_progress_div=False)
         self.w(form.form_render(renderer=renderer))
 
 
@@ -244,8 +241,8 @@
         submiturl = self.config['submit-url']
         submitmail = self.config['submit-mail']
         if submiturl or submitmail:
-            form = self.vreg.select_object('forms', 'base', self.req, rset=None,
-                                           mainform=False)
+            form = self.vreg.select('forms', 'base', self.req, rset=None,
+                                    mainform=False)
             binfo = text_error_description(ex, excinfo, req, eversion, cversions)
             form.form_add_hidden('description', binfo,
                                  # we must use a text area to keep line breaks
@@ -286,7 +283,7 @@
 
 class ProcessInformationView(StartupView):
     id = 'info'
-    __select__ = none_rset() & match_user_groups('managers')
+    __select__ = none_rset() & match_user_groups('users', 'managers')
 
     title = _('server information')
 
--- a/web/views/massmailing.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/massmailing.py	Fri Jul 31 14:25:30 2009 +0200
@@ -123,9 +123,9 @@
 
     def call(self):
         req = self.req
-        req.add_js('cubicweb.widgets.js')
+        req.add_js('cubicweb.widgets.js', 'cubicweb.massmailing.js')
         req.add_css('cubicweb.mailform.css')
         from_addr = '%s <%s>' % (req.user.dc_title(), req.user.get_email())
-        form = self.vreg.select_object('forms', 'massmailing', self.req, self.rset,
-                                       action='sendmail', domid='sendmail')
+        form = self.vreg.select('forms', 'massmailing', self.req, rset=self.rset,
+                                action='sendmail', domid='sendmail')
         self.w(form.form_render(sender=from_addr))
--- a/web/views/navigation.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/navigation.py	Fri Jul 31 14:25:30 2009 +0200
@@ -48,6 +48,7 @@
     def index_display(self, start, stop):
         return u'%s - %s' % (start+1, stop+1)
 
+
 class SortedNavigation(NavigationComponent):
     """sorted navigation apply if navigation is needed (according to page size)
     and if the result set is sorted
@@ -147,21 +148,21 @@
 
 def limit_rset_using_paged_nav(self, req, rset, w, forcedisplay=False,
                                show_all_option=True, page_size=None):
-    showall = forcedisplay or req.form.get('__force_display') is not None
-    nav = not showall and self.vreg.select_component('navigation', req, rset,
-                                                     page_size=page_size)
-    if nav:
-        # get boundaries before component rendering
-        start, stop = nav.page_boundaries()
-        nav.render(w=w)
-        params = dict(req.form)
-        nav.clean_params(params)
-        # make a link to see them all
-        if show_all_option:
-            url = xml_escape(self.build_url(__force_display=1, **params))
-            w(u'<p><a href="%s">%s</a></p>\n'
-              % (url, req._('show %s results') % len(rset)))
-        rset.limit(offset=start, limit=stop-start, inplace=True)
+    if not (forcedisplay or req.form.get('__force_display') is not None):
+        nav = self.vreg.select_object('components', 'navigation', req,
+                                      rset=rset, page_size=page_size)
+        if nav:
+            # get boundaries before component rendering
+            start, stop = nav.page_boundaries()
+            nav.render(w=w)
+            params = dict(req.form)
+            nav.clean_params(params)
+            # make a link to see them all
+            if show_all_option:
+                url = xml_escape(self.build_url(__force_display=1, **params))
+                w(u'<p><a href="%s">%s</a></p>\n'
+                  % (url, req._('show %s results') % len(rset)))
+            rset.limit(offset=start, limit=stop-start, inplace=True)
 
 
 # monkey patch base View class to add a .pagination(req, rset, w, forcedisplay)
--- a/web/views/owl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/owl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -6,14 +6,14 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import TransformError, xml_escape
 
 from cubicweb.view import StartupView, EntityView
+from cubicweb.selectors import none_rset, match_view
 from cubicweb.web.action import Action
-from cubicweb.selectors import none_rset, match_view
-
-_ = unicode
+from cubicweb.web.views import schema
 
 OWL_CARD_MAP = {'1': '<rdf:type rdf:resource="&owl;FunctionalProperty"/>',
                 '?': '<owl:maxCardinality rdf:datatype="&xsd;int">1</owl:maxCardinality>',
@@ -55,8 +55,6 @@
 
 OWL_CLOSING_ROOT = u'</rdf:RDF>'
 
-DEFAULT_SKIP_RELS = frozenset(('is', 'is_instance_of', 'identity',
-                               'owned_by', 'created_by'))
 
 class OWLView(StartupView):
     """This view export in owl format schema database. It is the TBOX"""
@@ -69,36 +67,36 @@
         skipmeta = int(self.req.form.get('skipmeta', True))
         if writeprefix:
             self.w(OWL_OPENING_ROOT % {'appid': self.schema.name})
-        self.visit_schema(skipmeta=skipmeta)
+        self.visit_schema(skiptypes=skipmeta and schema.SKIP_TYPES or ())
         if writeprefix:
             self.w(OWL_CLOSING_ROOT)
 
-    def visit_schema(self, skiprels=DEFAULT_SKIP_RELS, skipmeta=True):
+    def should_display_rschema(self, rschema):
+        return not rschema in self.skiptypes and (
+            rschema.has_local_role('read') or
+            rschema.has_perm(self.req, 'read'))
+
+    def visit_schema(self, skiptypes):
         """get a layout for a whole schema"""
-        entities = sorted([eschema for eschema in self.schema.entities()
-                           if not eschema.is_final()])
-        if skipmeta:
-            entities = [eschema for eschema in entities
-                        if not eschema.meta]
+        self.skiptypes = skiptypes
+        entities = sorted(eschema for eschema in self.schema.entities()
+                          if not eschema.is_final() or eschema in skiptypes)
         self.w(u'<!-- classes definition -->')
         for eschema in entities:
-            self.visit_entityschema(eschema, skiprels)
+            self.visit_entityschema(eschema)
             self.w(u'<!-- property definition -->')
-            self.visit_property_schema(eschema, skiprels)
+            self.visit_property_schema(eschema)
             self.w(u'<!-- datatype property -->')
             self.visit_property_object_schema(eschema)
 
-    def visit_entityschema(self, eschema, skiprels=()):
+    def visit_entityschema(self, eschema):
         """get a layout for an entity OWL schema"""
         self.w(u'<owl:Class rdf:ID="%s">'% eschema)
         self.w(u'<!-- relations -->')
         for rschema, targetschemas, role in eschema.relation_definitions():
-            if rschema.type in skiprels:
-                continue
-            if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
+            if not self.should_display_rschema(rschema):
                 continue
             for oeschema in targetschemas:
-                label = rschema.type
                 if role == 'subject':
                     card = rschema.rproperty(eschema, oeschema, 'cardinality')[0]
                 else:
@@ -110,58 +108,43 @@
   <owl:onProperty rdf:resource="#%s"/>
   %s
  </owl:Restriction>
-</rdfs:subClassOf>
-''' % (label, cardtag))
+</rdfs:subClassOf>''' % (rschema, cardtag))
 
         self.w(u'<!-- attributes -->')
-
         for rschema, aschema in eschema.attribute_definitions():
-            if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
-                continue
-            aname = rschema.type
-            if aname == 'eid':
+            if not self.should_display_rschema(rschema):
                 continue
             self.w(u'''<rdfs:subClassOf>
   <owl:Restriction>
    <owl:onProperty rdf:resource="#%s"/>
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
   </owl:Restriction>
-</rdfs:subClassOf>'''
-                   % aname)
+</rdfs:subClassOf>''' % rschema)
         self.w(u'</owl:Class>')
 
-    def visit_property_schema(self, eschema, skiprels=()):
+    def visit_property_schema(self, eschema):
         """get a layout for property entity OWL schema"""
         for rschema, targetschemas, role in eschema.relation_definitions():
-            if rschema.type in skiprels:
-                continue
-            if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
+            if not self.should_display_rschema(rschema):
                 continue
             for oeschema in targetschemas:
-                label = rschema.type
                 self.w(u'''<owl:ObjectProperty rdf:ID="%s">
  <rdfs:domain rdf:resource="#%s"/>
  <rdfs:range rdf:resource="#%s"/>
-</owl:ObjectProperty>
-''' % (label, eschema, oeschema.type))
+</owl:ObjectProperty>''' % (rschema, eschema, oeschema.type))
 
     def visit_property_object_schema(self, eschema):
         for rschema, aschema in eschema.attribute_definitions():
-            if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
-                continue
-            aname = rschema.type
-            if aname == 'eid':
+            if not self.should_display_rschema(rschema):
                 continue
             self.w(u'''<owl:DatatypeProperty rdf:ID="%s">
   <rdfs:domain rdf:resource="#%s"/>
   <rdfs:range rdf:resource="%s"/>
-</owl:DatatypeProperty>'''
-                   % (aname, eschema, OWL_TYPE_MAP[aschema.type]))
+</owl:DatatypeProperty>''' % (rschema, eschema, OWL_TYPE_MAP[aschema.type]))
 
 
 class OWLABOXView(EntityView):
     '''This view represents a part of the ABOX for a given entity.'''
-
     id = 'owlabox'
     title = _('owlabox')
     templatable = False
@@ -173,8 +156,8 @@
             self.cell_call(i, 0)
         self.w(OWL_CLOSING_ROOT)
 
-    def cell_call(self, row, col, skiprels=DEFAULT_SKIP_RELS):
-        self.wview('owlaboxitem', self.rset, row=row, col=col, skiprels=skiprels)
+    def cell_call(self, row, col):
+        self.wview('owlaboxitem', self.rset, row=row, col=col)
 
 
 class OWLABOXItemView(EntityView):
@@ -183,13 +166,13 @@
     templatable = False
     content_type = 'application/xml' # 'text/xml'
 
-    def cell_call(self, row, col, skiprels=DEFAULT_SKIP_RELS):
+    def cell_call(self, row, col):
         entity = self.complete_entity(row, col)
         eschema = entity.e_schema
         self.w(u'<%s rdf:ID="%s">' % (eschema, entity.eid))
         self.w(u'<!--attributes-->')
         for rschema, aschema in eschema.attribute_definitions():
-            if rschema.type in skiprels:
+            if rschema.meta:
                 continue
             if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
                 continue
@@ -204,7 +187,7 @@
                 pass
         self.w(u'<!--relations -->')
         for rschema, targetschemas, role in eschema.relation_definitions():
-            if rschema.type in skiprels:
+            if rschema.meta:
                 continue
             if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')):
                 continue
--- a/web/views/primary.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/primary.py	Fri Jul 31 14:25:30 2009 +0200
@@ -39,12 +39,11 @@
 
     def cell_call(self, row, col):
         self.row = row
-        # XXX move render_entity implementation here
-        self.render_entity(self.complete_entity(row, col))
         self.maxrelated = self.req.property_value('navigation.related-limit')
+        entity = self.complete_entity(row, col)
+        self.render_entity(entity)
 
     def render_entity(self, entity):
-        """return html to display the given entity"""
         self.render_entity_title(entity)
         self.render_entity_metadata(entity)
         # entity's attributes and relations, excluding meta data
@@ -54,23 +53,9 @@
             self.w(u'<table width="100%"><tr><td style="width: 75%">')
         self.w(u'<div class="mainInfo">')
         self.content_navigation_components('navcontenttop')
-        try:
-            self.render_entity_attributes(entity)
-        except TypeError, e: # XXX bw compat
-            if 'render_entity' not in e.args[0]:
-                raise
-            warn('siderelations argument of render_entity_attributes is '
-                 'deprecated (%s)' % self.__class__)
-            self.render_entity_attributes(entity, [])
+        self.render_entity_attributes(entity)
         if self.main_related_section:
-            try:
-                self.render_entity_relations(entity)
-            except TypeError, e: # XXX bw compat
-                if 'render_entity' not in e.args[0]:
-                    raise
-                warn('siderelations argument of render_entity_relations is '
-                     'deprecated')
-                self.render_entity_relations(entity, [])
+            self.render_entity_relations(entity)
         self.w(u'</div>')
         # side boxes
         if boxes or hasattr(self, 'render_side_related'):
@@ -84,11 +69,10 @@
             self.w(u'</td></tr></table>')
         self.content_navigation_components('navcontentbottom')
 
-
     def content_navigation_components(self, context):
         self.w(u'<div class="%s">' % context)
-        for comp in self.vreg.possible_vobjects('contentnavigation',
-                                                self.req, self.rset, row=self.row,
+        for comp in self.vreg.possible_vobjects('contentnavigation', self.req,
+                                                rset=self.rset, row=self.row,
                                                 view=self, context=context):
             try:
                 comp.render(w=self.w, row=self.row, view=self)
@@ -164,7 +148,7 @@
             label = display_name(self.req, rschema.type, role)
             vid = dispctrl.get('vid', 'sidebox')
             sideboxes.append( (label, rset, vid) )
-        sideboxes += self.vreg.possible_vobjects('boxes', self.req, self.rset,
+        sideboxes += self.vreg.possible_vobjects('boxes', self.req, rset=self.rset,
                                                  row=self.row, view=self,
                                                  context='incontext')
         return sideboxes
--- a/web/views/schema.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/schema.py	Fri Jul 31 14:25:30 2009 +0200
@@ -10,29 +10,179 @@
 from itertools import cycle
 
 from logilab.mtconverter import xml_escape
-from yams import schema2dot as s2d
+from yams import BASE_TYPES, schema2dot as s2d
 
-from cubicweb.selectors import implements, yes
+from cubicweb.selectors import implements, yes, match_user_groups
+from cubicweb.schema import META_RELATIONS_TYPES, SCHEMA_TYPES
 from cubicweb.schemaviewer import SchemaViewer
 from cubicweb.view import EntityView, StartupView
 from cubicweb.common import tags, uilib
-from cubicweb.web import action
-from cubicweb.web.views import TmpFileViewMixin, primary, baseviews, tabs
-from cubicweb.web.facet import AttributeFacet
+from cubicweb.web import action, facet
+from cubicweb.web.views import TmpFileViewMixin
+from cubicweb.web.views import primary, baseviews, tabs, management
+
+ALWAYS_SKIP_TYPES = BASE_TYPES | SCHEMA_TYPES
+SKIP_TYPES = ALWAYS_SKIP_TYPES | META_RELATIONS_TYPES
+SKIP_TYPES.update(set(('Transition', 'State', 'TrInfo',
+                       'CWUser', 'CWGroup',
+                       'CWCache', 'CWProperty', 'CWPermission',
+                       'ExternalUri')))
+
+def skip_types(req):
+    if int(req.form.get('skipmeta', True)):
+        return SKIP_TYPES
+    return ALWAYS_SKIP_TYPES
+
+# global schema view ###########################################################
+
+class SchemaView(tabs.TabsMixin, StartupView):
+    id = 'schema'
+    title = _('instance schema')
+    tabs = [_('schema-text'), _('schema-image')]
+    default_tab = 'schema-text'
+
+    def call(self):
+        """display schema information"""
+        self.req.add_js('cubicweb.ajax.js')
+        self.req.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
+        self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
+        self.render_tabs(self.tabs, self.default_tab)
+
+
+class SchemaTabImageView(StartupView):
+    id = 'schema-image'
+
+    def call(self):
+        self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
+                 u'meta-data, but you can also display a <a href="%s">complete '
+                 u'schema with meta-data</a>.</div>')
+               % xml_escape(self.build_url('view', vid='schemagraph', skipmeta=0)))
+        self.w(u'<img src="%s" alt="%s"/>\n' % (
+            xml_escape(self.req.build_url('view', vid='schemagraph', skipmeta=1)),
+            self.req._("graphical representation of the instance'schema")))
+
+
+class SchemaTabTextView(StartupView):
+    id = 'schema-text'
+
+    def call(self):
+        rset = self.req.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
+                                'X final FALSE')
+        self.wview('table', rset, displayfilter=True)
 
 
-class ViewSchemaAction(action.Action):
-    id = 'schema'
-    __select__ = yes()
+class ManagerSchemaPermissionsView(StartupView, management.SecurityViewMixIn):
+    id = 'schema-security'
+    __select__ = StartupView.__select__ & match_user_groups('managers')
+
+    def call(self, display_relations=True):
+        self.req.add_css('cubicweb.acl.css')
+        skiptypes = skip_types(self.req)
+        formparams = {}
+        formparams['sec'] = self.id
+        if not skiptypes:
+            formparams['skipmeta'] = u'0'
+        schema = self.schema
+        # compute entities
+        entities = sorted(eschema for eschema in schema.entities()
+                          if not (eschema.is_final() or eschema in skiptypes))
+        # compute relations
+        if display_relations:
+            relations = sorted(rschema for rschema in schema.relations()
+                               if not (rschema.is_final()
+                                       or rschema in skiptypes
+                                       or rschema in META_RELATIONS_TYPES))
+        else:
+            relations = []
+        # index
+        _ = self.req._
+        self.w(u'<div id="schema_security"><a id="index" href="index"/>')
+        self.w(u'<h2 class="schema">%s</h2>' % _('index').capitalize())
+        self.w(u'<h4>%s</h4>' %   _('Entities').capitalize())
+        ents = []
+        for eschema in sorted(entities):
+            url = xml_escape(self.build_url('schema', **formparams))
+            ents.append(u'<a class="grey" href="%s#%s">%s</a> (%s)' % (
+                url,  eschema.type, eschema.type, _(eschema.type)))
+        self.w(u', '.join(ents))
+        self.w(u'<h4>%s</h4>' % (_('relations').capitalize()))
+        rels = []
+        for rschema in sorted(relations):
+            url = xml_escape(self.build_url('schema', **formparams))
+            rels.append(u'<a class="grey" href="%s#%s">%s</a> (%s), ' %  (
+                url , rschema.type, rschema.type, _(rschema.type)))
+        self.w(u', '.join(ents))
+        # entities
+        self.display_entities(entities, formparams)
+        # relations
+        if relations:
+            self.display_relations(relations, formparams)
+        self.w(u'</div>')
 
-    title = _("site schema")
-    category = 'siteactions'
-    order = 30
+    def display_entities(self, entities, formparams):
+        _ = self.req._
+        self.w(u'<a id="entities" href="entities"/>')
+        self.w(u'<h2 class="schema">%s</h2>' % _('permissions for entities').capitalize())
+        for eschema in entities:
+            self.w(u'<a id="%s" href="%s"/>' %  (eschema.type, eschema.type))
+            self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
+            url = xml_escape(self.build_url('schema', **formparams) + '#index')
+            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (
+                url,  self.req.external_resource('UP_ICON'), _('up')))
+            self.w(u'</h3>')
+            self.w(u'<div style="margin: 0px 1.5em">')
+            self.schema_definition(eschema, link=False)
+            # display entity attributes only if they have some permissions modified
+            modified_attrs = []
+            for attr, etype in  eschema.attribute_definitions():
+                if self.has_schema_modified_permissions(attr, attr.ACTIONS):
+                    modified_attrs.append(attr)
+            if  modified_attrs:
+                self.w(u'<h4>%s</h4>' % _('attributes with modified permissions:').capitalize())
+                self.w(u'</div>')
+                self.w(u'<div style="margin: 0px 6em">')
+                for attr in  modified_attrs:
+                    self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attr.type, _(attr.type)))
+                    self.schema_definition(attr, link=False)
+            self.w(u'</div>')
 
-    def url(self):
-        return self.build_url(self.id)
+    def display_relations(self, relations, formparams):
+        _ = self.req._
+        self.w(u'<a id="relations" href="relations"/>')
+        self.w(u'<h2 class="schema">%s </h2>' % _('permissions for relations').capitalize())
+        for rschema in relations:
+            self.w(u'<a id="%s" href="%s"/>' %  (rschema.type, rschema.type))
+            self.w(u'<h3 class="schema">%s (%s) ' % (rschema.type, _(rschema.type)))
+            url = xml_escape(self.build_url('schema', **formparams) + '#index')
+            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (
+                url,  self.req.external_resource('UP_ICON'), _('up')))
+            self.w(u'</h3>')
+            self.w(u'<div style="margin: 0px 1.5em">')
+            subjects = [str(subj) for subj in rschema.subjects()]
+            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (
+                _('subject_plural:'),
+                ', '.join(str(subj) for subj in rschema.subjects()),
+                ', '.join(_(str(subj)) for subj in rschema.subjects())))
+            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (
+                _('object_plural:'),
+                ', '.join(str(obj) for obj in rschema.objects()),
+                ', '.join(_(str(obj)) for obj in rschema.objects())))
+            self.schema_definition(rschema, link=False)
+            self.w(u'</div>')
 
 
+class SchemaUreportsView(StartupView):
+    id = 'schema-block'
+
+    def call(self):
+        viewer = SchemaViewer(self.req)
+        layout = viewer.visit_schema(self.schema, display_relations=True,
+                                     skiptypes=skip_types(self.req))
+        self.w(uilib.ureport_as_html(layout))
+
+
+# CWAttribute / CWRelation #####################################################
+
 class CWRDEFPrimaryView(primary.PrimaryView):
     __select__ = implements('CWAttribute', 'CWRelation')
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
@@ -57,14 +207,11 @@
         if final:
             self.w(u'</em>')
 
-SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
-                'has_text',)
 
 class CWETypePrimaryView(tabs.TabsMixin, primary.PrimaryView):
     __select__ = implements('CWEType')
     title = _('in memory entity schema')
     main_related_section = False
-    skip_rels = SKIPPED_RELS
     tabs = [_('cwetype-schema-text'), _('cwetype-schema-image'),
             _('cwetype-schema-permissions'), _('cwetype-workflow')]
     default_tab = 'cwetype-schema-text'
@@ -183,94 +330,59 @@
 
 # schema images ###############################################################
 
-class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
-    def __init__(self, req):
-        # FIXME: colors are arbitrary
-        self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa',
-                                 '#000000', '#888888') ).next
+class RestrictedSchemaVisitorMixIn(object):
+    def __init__(self, req, *args, **kwargs):
         self.req = req
-
-    def display_attr(self, rschema):
-        return not rschema.meta and (rschema.has_local_role('read')
-                                     or rschema.has_perm(self.req, 'read'))
+        super(RestrictedSchemaVisitorMixIn, self).__init__(*args, **kwargs)
 
-    # XXX remove this method once yams > 0.20 is out
-    def node_properties(self, eschema):
-        """return default DOT drawing options for an entity schema"""
-        label = ['{', eschema.type, '|']
-        label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
-                                if rel.final and self.display_attr(rel)))
-        label.append(r'\l}') # trailing \l ensure alignement of the last one
-        return {'label' : ''.join(label), 'shape' : "record",
-                'fontname' : "Courier", 'style' : "filled"}
+    def should_display_schema(self, rschema):
+        return (super(RestrictedSchemaVisitorMixIn, self).should_display_schema(rschema)
+                and (rschema.has_local_role('read')
+                     or rschema.has_perm(self.req, 'read')))
 
-    def edge_properties(self, rschema, subjnode, objnode):
-        kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode)
-        # symetric rels are handled differently, let yams decide what's best
-        if not rschema.symetric:
-            kwargs['color'] = self.nextcolor()
-        kwargs['fontcolor'] = kwargs['color']
-        # dot label decoration is just awful (1 line underlining the label
-        # + 1 line going to the closest edge spline point)
-        kwargs['decorate'] = 'false'
-        return kwargs
+    def should_display_attr(self, rschema):
+        return (super(RestrictedSchemaVisitorMixIn, self).should_display_attr(rschema)
+                and (rschema.has_local_role('read')
+                     or rschema.has_perm(self.req, 'read')))
 
 
-class RestrictedSchemaVisitorMiIn:
-    def __init__(self, req, *args, **kwargs):
-        # hack hack hack
-        assert len(self.__class__.__bases__) == 2
-        self.__parent = self.__class__.__bases__[1]
-        self.__parent.__init__(self, *args, **kwargs)
-        self.req = req
-
-    def nodes(self):
-        for etype, eschema in self.__parent.nodes(self):
-            if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
-                yield eschema.type, eschema
-
-    def edges(self):
-        for setype, oetype, rschema in self.__parent.edges(self):
-            if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
-                yield setype, oetype, rschema
-
-
-class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
+class FullSchemaVisitor(RestrictedSchemaVisitorMixIn, s2d.FullSchemaVisitor):
     pass
 
-class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
+class OneHopESchemaVisitor(RestrictedSchemaVisitorMixIn,
+                           s2d.OneHopESchemaVisitor):
     pass
 
-class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
+class OneHopRSchemaVisitor(RestrictedSchemaVisitorMixIn,
+                           s2d.OneHopRSchemaVisitor):
     pass
 
 
 class SchemaImageView(TmpFileViewMixin, StartupView):
     id = 'schemagraph'
+    content_type = 'image/png'
 
-    content_type = 'image/png'
-    skip_rels = SKIPPED_RELS
     def _generate(self, tmpfile):
         """display global schema information"""
-        skipmeta = not int(self.req.form.get('withmeta', 0))
-        visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+        print 'skipedtypes', skip_types(self.req)
+        visitor = FullSchemaVisitor(self.req, self.schema,
+                                    skiptypes=skip_types(self.req))
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
+
 
 class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
     id = 'schemagraph'
     __select__ = implements('CWEType')
-
     content_type = 'image/png'
-    skip_rels = SKIPPED_RELS
 
     def _generate(self, tmpfile):
         """display schema information for an entity"""
         entity = self.entity(self.row, self.col)
         eschema = self.vreg.schema.eschema(entity.name)
-        visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+        visitor = OneHopESchemaVisitor(self.req, eschema,
+                                       skiptypes=skip_types(self.req))
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
+
 
 class CWRTypeSchemaImageView(CWETypeSchemaImageView):
     __select__ = implements('CWRType')
@@ -280,12 +392,23 @@
         entity = self.entity(self.row, self.col)
         rschema = self.vreg.schema.rschema(entity.name)
         visitor = OneHopRSchemaVisitor(self.req, rschema)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
+
+
+# misc: facets, actions ########################################################
 
-### facets
+class CWFinalFacet(facet.AttributeFacet):
+    id = 'cwfinal-facet'
+    __select__ = facet.AttributeFacet.__select__ & implements('CWEType', 'CWRType')
+    rtype = 'final'
 
-class CWFinalFacet(AttributeFacet):
-    id = 'cwfinal-facet'
-    __select__ = AttributeFacet.__select__ & implements('CWEType', 'CWRType')
-    rtype = 'final'
+class ViewSchemaAction(action.Action):
+    id = 'schema'
+    __select__ = yes()
+
+    title = _("site schema")
+    category = 'siteactions'
+    order = 30
+
+    def url(self):
+        return self.build_url(self.id)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/sparql.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,122 @@
+"""SPARQL integration
+
+:organization: Logilab
+:copyright: 2009 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
+"""
+v__docformat__ = "restructuredtext en"
+
+import rql
+from yams import xy
+
+from lxml import etree
+from lxml.builder import E
+
+from cubicweb.view import StartupView, AnyRsetView
+from cubicweb.web import Redirect, form, formfields, formwidgets as fwdgs
+from cubicweb.web.views import forms, urlrewrite
+try:
+    from cubicweb.spa2rql import Sparql2rqlTranslator
+except ImportError:
+    # fyzz not available (only a recommends)
+    Sparql2rqlTranslator = None
+
+class SparqlForm(forms.FieldsForm):
+    id = 'sparql'
+    sparql = formfields.StringField(help=_('type here a sparql query'))
+    resultvid = formfields.StringField(choices=((_('table'), 'table'),
+                                                (_('sparql xml'), 'sparqlxml')),
+                                       widget=fwdgs.Radio,
+                                       initial='table')
+    form_buttons = [fwdgs.SubmitButton()]
+    @property
+    def action(self):
+        return self.req.url()
+
+
+class SparqlFormView(form.FormViewMixIn, StartupView):
+    id = 'sparql'
+    def call(self):
+        form = self.vreg.select('forms', 'sparql', self.req)
+        self.w(form.form_render())
+        sparql = self.req.form.get('sparql')
+        vid = self.req.form.get('resultvid', 'table')
+        if sparql:
+            try:
+                qinfo = Sparql2rqlTranslator(self.schema).translate(sparql)
+            except rql.TypeResolverException, ex:
+                self.w(self.req._('can not resolve entity types:') + u' ' + unicode('ex'))
+            except UnsupportedQuery:
+                self.w(self.req._('we are not yet ready to handle this query'))
+            except xy.UnsupportedVocabulary, ex:
+                self.w(self.req._('unknown vocabulary:') + u' ' + unicode('ex'))
+            if vid == 'sparqlxml':
+                url = self.build_url('view', rql=qinfo.finalize(), vid=vid)
+                raise Redirect(url)
+            rset = self.req.execute(qinfo.finalize())
+            self.wview(vid, rset, 'null')
+
+
+## sparql resultset views #####################################################
+
+YAMS_XMLSCHEMA_MAPPING = {
+    'String': 'string',
+    'Int': 'integer',
+    'Float': 'float',
+    'Boolean': 'boolean',
+    'Datetime': 'dateTime',
+    'Date': 'date',
+    'Time': 'time',
+    # XXX the following types don't have direct mapping
+    'Decimal': 'string',
+    'Interval': 'duration',
+    'Password': 'string',
+    'Bytes': 'base64Binary',
+    }
+
+def xmlschema(yamstype):
+    return 'http://www.w3.org/2001/XMLSchema#%s' % YAMS_XMLSCHEMA_MAPPING[yamstype]
+
+class SparqlResultXmlView(AnyRsetView):
+    """The spec can be found here: http://www.w3.org/TR/rdf-sparql-XMLres/
+    """
+    id = 'sparqlxml'
+    content_type = 'application/sparql-results+xml'
+    templatable = False
+
+    def call(self):
+        # XXX handle UNION
+        rqlst = self.rset.syntax_tree().children[0]
+        varnames = [var.name for var in rqlst.selection]
+        results = E.results()
+        for rowidx in xrange(len(self.rset)):
+            result = E.result()
+            for colidx, varname in enumerate(varnames):
+                result.append(self.cell_binding(rowidx, colidx, varname))
+            results.append(result)
+        sparql = E.sparql(E.head(*(E.variable(name=name) for name in varnames)),
+                          results)
+        self.w(u'<?xml version="1.0"?>\n')
+        self.w(etree.tostring(sparql, encoding=unicode, pretty_print=True))
+
+    def cell_binding(self, row, col, varname):
+        celltype = self.rset.description[row][col]
+        if self.schema.eschema(celltype).is_final():
+            cellcontent = self.view('cell', self.rset, row=row, col=col)
+            return E.binding(E.literal(cellcontent,
+                                       datatype=xmlschema(celltype)),
+                             name=varname)
+        else:
+            entity = self.entity(row, col)
+            return E.binding(E.uri(entity.absolute_url()), name=varname)
+
+    def set_request_content_type(self):
+        """overriden to set the correct filetype and filename"""
+        self.req.set_content_type(self.content_type,
+                                  filename='sparql.xml',
+                                  encoding=self.req.encoding)
+
+def registration_callback(vreg):
+    if Sparql2rqlTranslator is not None:
+        vreg.register_all(globals().values(), __name__)
--- a/web/views/startup.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/startup.py	Fri Jul 31 14:25:30 2009 +0200
@@ -15,14 +15,11 @@
 from cubicweb.view import StartupView
 from cubicweb.selectors import match_user_groups, implements
 from cubicweb.schema import display_name
-from cubicweb.common.uilib import ureport_as_html
 from cubicweb.web import ajax_replace_url, uicfg, httpcache
-from cubicweb.web.views import tabs
-from cubicweb.web.views.management import SecurityViewMixIn
 
 class ManageView(StartupView):
     id = 'manage'
-    title = _('view_manage')
+    title = _('manage')
     http_cache_manager = httpcache.EtagHTTPCacheManager
 
     @classmethod
@@ -32,8 +29,6 @@
                 uicfg.indexview_etype_section.setdefault(eschema, 'schema')
             elif eschema.is_subobject(strict=True):
                 uicfg.indexview_etype_section.setdefault(eschema, 'subobject')
-            elif eschema.meta:
-                uicfg.indexview_etype_section.setdefault(eschema, 'system')
             else:
                 uicfg.indexview_etype_section.setdefault(eschema, 'application')
 
@@ -41,7 +36,7 @@
         return False
 
     def call(self, **kwargs):
-        """The default view representing the application's management"""
+        """The default view representing the instance's management"""
         self.req.add_css('cubicweb.manageview.css')
         self.w(u'<div>\n')
         if not self.display_folders():
@@ -81,7 +76,7 @@
 
     def folders(self):
         self.w(u'<h4>%s</h4>\n' % self.req._('Browse by category'))
-        self.vreg.select_view('tree', self.req, None).render(w=self.w)
+        self.vreg.select('views', 'tree', self.req).render(w=self.w)
 
     def startup_views(self):
         self.w(u'<h4>%s</h4>\n' % self.req._('Startup views'))
@@ -140,11 +135,7 @@
             etype = eschema.type
             label = display_name(req, etype, 'plural')
             nb = req.execute('Any COUNT(X) WHERE X is %s' % etype)[0][0]
-            if nb > 1:
-                view = self.vreg.select_view('list', req, req.etype_rset(etype))
-                url = view.url()
-            else:
-                url = self.build_url('view', rql='%s X' % etype)
+            url = self.build_url(etype)
             etypelink = u'&nbsp;<a href="%s">%s</a> (%d)' % (
                 xml_escape(url), label, nb)
             yield (label, etypelink, self.add_entity_link(eschema, req))
@@ -165,157 +156,3 @@
     def display_folders(self):
         return 'Folder' in self.schema and self.req.execute('Any COUNT(X) WHERE X is Folder')[0][0]
 
-class SchemaView(tabs.TabsMixin, StartupView):
-    id = 'schema'
-    title = _('application schema')
-    tabs = [_('schema-text'), _('schema-image')]
-    default_tab = 'schema-text'
-
-    def call(self):
-        """display schema information"""
-        self.req.add_js('cubicweb.ajax.js')
-        self.req.add_css(('cubicweb.schema.css','cubicweb.acl.css'))
-        self.w(u'<h1>%s</h1>' % _('Schema of the data model'))
-        self.render_tabs(self.tabs, self.default_tab)
-
-
-class SchemaTabImageView(StartupView):
-    id = 'schema-image'
-
-    def call(self):
-        self.w(_(u'<div>This schema of the data model <em>excludes</em> the '
-                 u'meta-data, but you can also display a <a href="%s">complete '
-                 u'schema with meta-data</a>.</div>')
-               % xml_escape(self.build_url('view', vid='schemagraph', withmeta=1)))
-        self.w(u'<img src="%s" alt="%s"/>\n' % (
-            xml_escape(self.req.build_url('view', vid='schemagraph', withmeta=0)),
-            self.req._("graphical representation of the application'schema")))
-
-
-class SchemaTabTextView(StartupView):
-    id = 'schema-text'
-
-    def call(self):
-        rset = self.req.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
-                                'X final FALSE')
-        self.wview('table', rset, displayfilter=True)
-
-
-class ManagerSchemaPermissionsView(StartupView, SecurityViewMixIn):
-    id = 'schema-security'
-    __select__ = StartupView.__select__ & match_user_groups('managers')
-
-    def call(self, display_relations=True,
-             skiprels=('is', 'is_instance_of', 'identity', 'owned_by', 'created_by')):
-        self.req.add_css('cubicweb.acl.css')
-        _ = self.req._
-        formparams = {}
-        formparams['sec'] = self.id
-        formparams['withmeta'] = int(self.req.form.get('withmeta', True))
-        schema = self.schema
-        # compute entities
-        entities = [eschema for eschema in schema.entities()
-                   if not eschema.is_final()]
-        if not formparams['withmeta']:
-            entities = [eschema for eschema in entities
-                        if not eschema.meta]
-        # compute relations
-        if display_relations:
-            relations = [rschema for rschema in schema.relations()
-                         if not (rschema.is_final() or rschema.type in skiprels)]
-            if not formparams['withmeta']:
-                relations = [rschema for rschema in relations
-                             if not rschema.meta]
-        else:
-            relations = []
-        # index
-        self.w(u'<div id="schema_security"><a id="index" href="index"/>')
-        self.w(u'<h2 class="schema">%s</h2>' % _('index').capitalize())
-        self.w(u'<h4>%s</h4>' %   _('Entities').capitalize())
-        ents = []
-        for eschema in sorted(entities):
-            url = xml_escape(self.build_url('schema', **formparams))
-            ents.append(u'<a class="grey" href="%s#%s">%s</a> (%s)' % (
-                url,  eschema.type, eschema.type, _(eschema.type)))
-        self.w(u', '.join(ents))
-        self.w(u'<h4>%s</h4>' % (_('relations').capitalize()))
-        rels = []
-        for rschema in sorted(relations):
-            url = xml_escape(self.build_url('schema', **formparams))
-            rels.append(u'<a class="grey" href="%s#%s">%s</a> (%s), ' %  (
-                url , rschema.type, rschema.type, _(rschema.type)))
-        self.w(u', '.join(ents))
-        # entities
-        self.display_entities(entities, formparams)
-        # relations
-        if relations:
-            self.display_relations(relations, formparams)
-        self.w(u'</div>')
-
-    def display_entities(self, entities, formparams):
-        _ = self.req._
-        self.w(u'<a id="entities" href="entities"/>')
-        self.w(u'<h2 class="schema">%s</h2>' % _('permissions for entities').capitalize())
-        for eschema in sorted(entities):
-            self.w(u'<a id="%s" href="%s"/>' %  (eschema.type, eschema.type))
-            self.w(u'<h3 class="schema">%s (%s) ' % (eschema.type, _(eschema.type)))
-            url = xml_escape(self.build_url('schema', **formparams) + '#index')
-            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (url,  self.req.external_resource('UP_ICON'), _('up')))
-            self.w(u'</h3>')
-            self.w(u'<div style="margin: 0px 1.5em">')
-            self.schema_definition(eschema, link=False)
-
-            # display entity attributes only if they have some permissions modified
-            modified_attrs = []
-            for attr, etype in  eschema.attribute_definitions():
-                if self.has_schema_modified_permissions(attr, attr.ACTIONS):
-                    modified_attrs.append(attr)
-            if  modified_attrs:
-                self.w(u'<h4>%s</h4>' % _('attributes with modified permissions:').capitalize())
-                self.w(u'</div>')
-                self.w(u'<div style="margin: 0px 6em">')
-                for attr in  modified_attrs:
-                    self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attr.type, _(attr.type)))
-                    self.schema_definition(attr, link=False)
-                self.w(u'</div>')
-            else:
-                self.w(u'</div>')
-
-
-    def display_relations(self, relations, formparams):
-        _ = self.req._
-        self.w(u'<a id="relations" href="relations"/>')
-        self.w(u'<h2 class="schema">%s </h2>' % _('permissions for relations').capitalize())
-        for rschema in sorted(relations):
-            self.w(u'<a id="%s" href="%s"/>' %  (rschema.type, rschema.type))
-            self.w(u'<h3 class="schema">%s (%s) ' % (rschema.type, _(rschema.type)))
-            url = xml_escape(self.build_url('schema', **formparams) + '#index')
-            self.w(u'<a href="%s"><img src="%s" alt="%s"/></a>' % (url,  self.req.external_resource('UP_ICON'), _('up')))
-            self.w(u'</h3>')
-            self.w(u'<div style="margin: 0px 1.5em">')
-            subjects = [str(subj) for subj in rschema.subjects()]
-            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (_('subject_plural:'),
-                                                ', '.join( [str(subj) for subj in rschema.subjects()]),
-                                                ', '.join( [_(str(subj)) for subj in rschema.subjects()])))
-            self.w(u'<div><strong>%s</strong> %s (%s)</div>' % (_('object_plural:'),
-                                                ', '.join( [str(obj) for obj in rschema.objects()]),
-                                                ', '.join( [_(str(obj)) for obj in rschema.objects()])))
-            self.schema_definition(rschema, link=False)
-            self.w(u'</div>')
-
-
-class SchemaUreportsView(StartupView):
-    id = 'schematext'
-
-    def call(self):
-        from cubicweb.schemaviewer import SchemaViewer
-        skipmeta = int(self.req.form.get('skipmeta', True))
-        schema = self.schema
-        viewer = SchemaViewer(self.req)
-        layout = viewer.visit_schema(schema, display_relations=True,
-                                     skiprels=('is', 'is_instance_of', 'identity',
-                                               'owned_by', 'created_by'),
-                                     skipmeta=skipmeta)
-        self.w(ureport_as_html(layout))
-
-
--- a/web/views/tableview.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/tableview.py	Fri Jul 31 14:25:30 2009 +0200
@@ -35,7 +35,7 @@
         rqlst.save_state()
         mainvar, baserql = prepare_facets_rqlst(rqlst, self.rset.args)
         wdgs = [facet.get_widget() for facet in self.vreg.possible_vobjects(
-            'facets', self.req, self.rset, context='tablefilter',
+            'facets', self.req, rset=self.rset, context='tablefilter',
             filtered_variable=mainvar)]
         wdgs = [wdg for wdg in wdgs if wdg is not None]
         rqlst.recover()
--- a/web/views/tabs.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/tabs.py	Fri Jul 31 14:25:30 2009 +0200
@@ -86,7 +86,7 @@
         selected_tabs = []
         for tab in tabs:
             try:
-                self.vreg.select_view(tab, self.req, self.rset)
+                self.vreg.select('views', tab, self.req, rset=self.rset)
                 selected_tabs.append(tab)
             except NoSelectableObject:
                 continue
@@ -98,8 +98,8 @@
         if entity and len(self.rset) > 1:
             entity.view(default, w=self.w)
             return
-        self.req.add_css('ui.tabs.css')
-        self.req.add_js(('ui.core.js', 'ui.tabs.js',
+        self.req.add_css('tabs-no-images.css')
+        self.req.add_js(('jquery.tools.min.js', 'cubicweb.htmlhelpers.js',
                          'cubicweb.ajax.js', 'cubicweb.tabs.js', 'cubicweb.lazy.js'))
         # prune tabs : not all are to be shown
         tabs = self.prune_tabs(tabs)
@@ -107,12 +107,9 @@
         active_tab = self.active_tab(tabs, default)
         # build the html structure
         w = self.w
-        if entity:
-            w(u'<div id="entity-tabs-%s">' % entity.eid)
-        else:
-            uid = make_uid('tab')
-            w(u'<div id="entity-tabs-%s">' % uid)
-        w(u'<ul>')
+        uid = entity and entity.eid or make_uid('tab')
+        w(u'<div id="entity-tabs-%s">' % uid)
+        w(u'<ul class="css-tabs" id="tabs-%s">' % uid)
         for tab in tabs:
             w(u'<li>')
             w(u'<a href="#as-%s">' % tab)
@@ -123,22 +120,26 @@
             w(u'</li>')
         w(u'</ul>')
         w(u'</div>')
+        w(u'<div id="panes-%s">' % uid)
         for tab in tabs:
-            w(u'<div id="as-%s">' % tab)
+            w(u'<div>')
             if entity:
                 self.lazyview(tab, eid=entity.eid)
             else:
                 self.lazyview(tab, static=True)
             w(u'</div>')
+        w(u'</div>')
         # call the set_tab() JS function *after* each tab is generated
         # because the callback binding needs to be done before
-        self.req.add_onload(u"""
-   jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s });
-   set_tab('%(vid)s', '%(cookiename)s');
- """ % {'tabindex'   : tabs.index(active_tab),
-        'vid'        : active_tab,
-        'eeid'       : (entity and entity.eid or uid),
-        'cookiename' : self.cookie_name})
+        # XXX make work history: true
+        self.req.add_onload(u'''
+    jQuery(function() {
+      jQuery("#tabs-%(eeid)s").tabs("#panes-%(eeid)s > div", {initialIndex: %(tabindex)s});
+      set_tab('%(vid)s', '%(cookiename)s');
+    });''' % {'eeid' : (entity and entity.eid or uid),
+              'vid'  : active_tab,
+              'cookiename' : self.cookie_name,
+              'tabindex' : tabs.index(active_tab)})
 
 
 class EntityRelationView(EntityView):
--- a/web/views/timetable.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/timetable.py	Fri Jul 31 14:25:30 2009 +0200
@@ -138,7 +138,7 @@
         for user, width in zip(users, widths):
             self.w(u'<th colspan="%s">' % max(MIN_COLS, width))
             if user != u"*":
-                user.view('secondary', w=self.w)
+                user.view('oneline', w=self.w)
             else:
                 self.w(user)
             self.w(u'</th>')
--- a/web/views/treeview.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/treeview.py	Fri Jul 31 14:25:30 2009 +0200
@@ -16,7 +16,7 @@
 from cubicweb.view import EntityView
 
 def treecookiename(treeid):
-    return str('treestate-%s' % treeid)
+    return str('%s-treestate' % treeid)
 
 class TreeView(EntityView):
     id = 'treeview'
@@ -26,7 +26,7 @@
 
     def call(self, subvid=None, treeid=None, initial_load=True):
         if subvid is None:
-            subvid = self.req.form.pop('subvid', 'oneline') # consume it
+            subvid = self.req.form.pop('treesubvid', 'oneline') # consume it
         if treeid is None:
             treeid = self.req.form.pop('treeid', None)
             if treeid is None:
@@ -117,7 +117,7 @@
                                              pageid=self.req.pageid,
                                              treeid=treeid,
                                              fname='view',
-                                             subvid=vid))
+                                             treesubvid=vid))
             divclasses = ['hitarea']
             if is_open:
                 liclasses.append('collapsable')
--- a/web/views/urlpublishing.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/urlpublishing.py	Fri Jul 31 14:25:30 2009 +0200
@@ -233,7 +233,7 @@
                 continue
             else:
                 try:
-                    action = self.vreg.select(actions, req, rset)
+                    action = self.vreg.select_best(actions, req, rset=rset)
                 except RegistryException:
                     raise PathDontMatch()
                 else:
--- a/web/views/urlrewrite.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/urlrewrite.py	Fri Jul 31 14:25:30 2009 +0200
@@ -76,9 +76,11 @@
         ('/index', dict(vid='index')),
         ('/myprefs', dict(vid='propertiesform')),
         ('/siteconfig', dict(vid='systempropertiesform')),
+        ('/siteinfo', dict(vid='info')),
         ('/manage', dict(vid='manage')),
         ('/notfound', dict(vid='404')),
         ('/error', dict(vid='error')),
+        ('/sparql', dict(vid='sparql')),
         (rgx('/schema/([^/]+?)/?'),  dict(vid='eschema', rql=r'Any X WHERE X is CWEType, X name "\1"')),
         (rgx('/add/([^/]+?)/?'), dict(vid='creation', etype=r'\1')),
         (rgx('/doc/images/(.+?)/?'), dict(vid='wdocimages', fid=r'\1')),
--- a/web/views/workflow.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/workflow.py	Fri Jul 31 14:25:30 2009 +0200
@@ -18,6 +18,7 @@
 from cubicweb.selectors import (implements, has_related_entities,
                                 relation_possible, match_form_params)
 from cubicweb.interfaces import IWorkflowable
+from cubicweb.view import EntityView
 from cubicweb.web import stdmsgs, action, component, form
 from cubicweb.web.form import FormViewMixIn
 from cubicweb.web.formfields import StringField,  RichTextField
@@ -51,10 +52,9 @@
         transition = self.req.eid_rset(self.req.form['treid']).get_entity(0, 0)
         dest = transition.destination()
         _ = self.req._
-        form = self.vreg.select_object('forms', 'changestate', self.req,
-                                       self.rset, row=row, col=col,
-                                       entity=entity,
-                                       redirect_path=self.redirectpath(entity))
+        form = self.vreg.select('forms', 'changestate', self.req, rset=self.rset,
+                                row=row, col=col, entity=entity,
+                                redirect_path=self.redirectpath(entity))
         self.w(form.error_message())
         self.w(u'<h4>%s %s</h4>\n' % (_(transition.name),
                                       entity.view('oneline')))
@@ -68,12 +68,9 @@
         return entity.rest_path()
 
 
-class WFHistoryVComponent(component.EntityVComponent):
-    """display the workflow history for entities supporting it"""
+class WFHistoryView(EntityView):
     id = 'wfhistory'
-    __select__ = (component.EntityVComponent.__select__
-                  & relation_possible('wf_info_for', role='object'))
-    context = 'navcontentbottom'
+    __select__ = relation_possible('wf_info_for', role='object')
     title = _('Workflow history')
 
     def cell_call(self, row, col, view=None):
@@ -103,6 +100,16 @@
                        displaycols=displaycols, headers=headers)
 
 
+class WFHistoryVComponent(component.EntityVComponent):
+    """display the workflow history for entities supporting it"""
+    id = 'wfhistory'
+    __select__ = WFHistoryView.__select__ & component.EntityVComponent.__select__
+    context = 'navcontentbottom'
+    title = _('Workflow history')
+
+    def cell_call(self, row, col, view=None):
+        self.wview('wfhistory', self.rset, row=row, col=col, view=view)
+
 # workflow entity types views #################################################
 
 class CellView(view.EntityView):
@@ -110,7 +117,7 @@
     __select__ = implements('TrInfo')
 
     def cell_call(self, row, col, cellvid=None):
-        self.w(self.entity(row, col).printable_value('comment'))
+        self.w(self.entity(row, col).view('reledit', rtype='comment'))
 
 
 class StateInContextView(view.EntityView):
--- a/web/views/xmlrss.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/views/xmlrss.py	Fri Jul 31 14:25:30 2009 +0200
@@ -13,11 +13,9 @@
 from logilab.mtconverter import xml_escape
 
 from cubicweb.selectors import non_final_entity, one_line_rset, appobject_selectable
-from cubicweb.view import EntityView, AnyRsetView
-from cubicweb.web.httpcache import MaxAgeHTTPCacheManager
-from cubicweb.web.component import Component
-from cubicweb.web.box import BoxTemplate
+from cubicweb.view import EntityView, AnyRsetView, Component
 from cubicweb.common.uilib import simple_sgml_tag
+from cubicweb.web import httpcache, box
 
 
 # base xml views ##############################################################
@@ -122,10 +120,10 @@
         return self.entity(0, 0).rss_feed_url()
 
 
-class RSSIconBox(BoxTemplate):
+class RSSIconBox(box.BoxTemplate):
     """just display the RSS icon on uniform result set"""
     id = 'rss'
-    __select__ = (BoxTemplate.__select__
+    __select__ = (box.BoxTemplate.__select__
                   & appobject_selectable('components', 'rss_feed_url'))
 
     visible = False
@@ -137,7 +135,8 @@
         except KeyError:
             self.error('missing RSS_LOGO external resource')
             return
-        urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
+        urlgetter = self.vreg.select('components', 'rss_feed_url',
+                                     self.req, rset=self.rset)
         url = urlgetter.feed_url()
         self.w(u'<a href="%s"><img src="%s" alt="rss"/></a>\n' % (xml_escape(url), rss))
 
@@ -147,7 +146,7 @@
     title = _('rss')
     templatable = False
     content_type = 'text/xml'
-    http_cache_manager = MaxAgeHTTPCacheManager
+    http_cache_manager = httpcache.MaxAgeHTTPCacheManager
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
 
     def _open(self):
--- a/web/webconfig.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/webconfig.py	Fri Jul 31 14:25:30 2009 +0200
@@ -1,4 +1,4 @@
-"""common web configuration for twisted/modpython applications
+"""common web configuration for twisted/modpython instances
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -60,7 +60,7 @@
 
 
 class WebConfiguration(CubicWebConfiguration):
-    """the WebConfiguration is a singleton object handling application's
+    """the WebConfiguration is a singleton object handling instance's
     configuration and preferences
     """
     cubicweb_vobject_path = CubicWebConfiguration.cubicweb_vobject_path | set(['web/views'])
@@ -83,13 +83,13 @@
         ('query-log-file',
          {'type' : 'string',
           'default': None,
-          'help': 'web application query log file',
+          'help': 'web instance query log file',
           'group': 'main', 'inputlevel': 2,
           }),
-        ('pyro-application-id',
+        ('pyro-instance-id',
          {'type' : 'string',
-          'default': Method('default_application_id'),
-          'help': 'CubicWeb application identifier in the Pyro name server',
+          'default': Method('default_instance_id'),
+          'help': 'CubicWeb instance identifier in the Pyro name server',
           'group': 'pyro-client', 'inputlevel': 1,
           }),
         # web configuration
@@ -145,6 +145,13 @@
           'sessions. Default to 2 min.',
           'group': 'web', 'inputlevel': 2,
           }),
+        ('force-html-content-type',
+         {'type' : 'yn',
+          'default': False,
+          'help': 'force text/html content type for your html pages instead of cubicweb user-agent based'\
+          'deduction of an appropriate content type',
+          'group': 'web', 'inputlevel': 2,
+          }),
         ('embed-allowed',
          {'type' : 'regexp',
           'default': None,
@@ -156,7 +163,7 @@
         ('submit-url',
          {'type' : 'string',
           'default': Method('default_submit_url'),
-          'help': ('URL that may be used to report bug in this application '
+          'help': ('URL that may be used to report bug in this instance '
                    'by direct access to the project\'s (jpl) tracker, '
                    'if you want this feature on. The url should looks like '
                    'http://mytracker.com/view?__linkto=concerns:1234:subject&etype=Ticket&type=bug&vid=creation '
@@ -168,7 +175,7 @@
         ('submit-mail',
          {'type' : 'string',
           'default': None,
-          'help': ('Mail used as recipient to report bug in this application, '
+          'help': ('Mail used as recipient to report bug in this instance, '
                    'if you want this feature on'),
           'group': 'web', 'inputlevel': 2,
           }),
@@ -215,7 +222,7 @@
     # don't use @cached: we want to be able to disable it while this must still
     # be cached
     def repository(self, vreg=None):
-        """return the application's repository object"""
+        """return the instance's repository object"""
         try:
             return self.__repo
         except AttributeError:
@@ -223,7 +230,7 @@
             if self.repo_method == 'inmemory':
                 repo = get_repository('inmemory', vreg=vreg, config=self)
             else:
-                repo = get_repository('pyro', self['pyro-application-id'],
+                repo = get_repository('pyro', self['pyro-instance-id'],
                                       config=self)
             self.__repo = repo
             return repo
@@ -298,7 +305,7 @@
                 yield join(fpath)
 
     def load_configuration(self):
-        """load application's configuration files"""
+        """load instance's configuration files"""
         super(WebConfiguration, self).load_configuration()
         # load external resources definition
         self._build_ext_resources()
--- a/web/webctl.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/web/webctl.py	Fri Jul 31 14:25:30 2009 +0200
@@ -27,4 +27,4 @@
             config.global_set_option('anonymous-password', 'anon')
 
     def postcreate(self):
-        """hooks called once application's initialization has been completed"""
+        """hooks called once instance's initialization has been completed"""
--- a/wsgi/handler.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/wsgi/handler.py	Fri Jul 31 14:25:30 2009 +0200
@@ -97,10 +97,7 @@
 #         assert self.base_url[-1] == '/'
 #         self.https_url = config['https-url']
 #         assert not self.https_url or self.https_url[-1] == '/'
-        try:
-            self.url_rewriter = self.appli.vreg.select_component('urlrewriter')
-        except ObjectNotFound:
-            self.url_rewriter = None
+        self.url_rewriter = self.appli.vreg.select_object('components', 'urlrewriter')
 
     def _render(self, req):
         """this function performs the actual rendering
--- a/wsgi/request.py	Fri Jul 31 14:18:53 2009 +0200
+++ b/wsgi/request.py	Fri Jul 31 14:25:30 2009 +0200
@@ -34,7 +34,7 @@
         self._headers = dict([(normalize_header(k[5:]), v) for k, v in self.environ.items()
                               if k.startswith('HTTP_')])
         https = environ.get("HTTPS") in ('yes', 'on', '1')
-        self._base_url = base_url or self.application_uri()
+        self._base_url = base_url or self.instance_uri()
         post, files = self.get_posted_data()
         super(CubicWebWsgiRequest, self).__init__(vreg, https, post)
         if files is not None:
@@ -63,7 +63,7 @@
 
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
-        to the application's root, but some other normalization may be needed
+        to the instance's root, but some other normalization may be needed
         so that the returned path may be used to compare to generated urls
 
         :param includeparams:
@@ -105,10 +105,10 @@
 
     ## wsgi request helpers ###################################################
 
-    def application_uri(self):
-        """Return the application's base URI (no PATH_INFO or QUERY_STRING)
+    def instance_uri(self):
+        """Return the instance's base URI (no PATH_INFO or QUERY_STRING)
 
-        see python2.5's wsgiref.util.application_uri code
+        see python2.5's wsgiref.util.instance_uri code
         """
         environ = self.environ
         url = environ['wsgi.url_scheme'] + '://'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xy.py	Fri Jul 31 14:25:30 2009 +0200
@@ -0,0 +1,20 @@
+"""map standard cubicweb schema to xml vocabularies
+
+:organization: Logilab
+:copyright: 2009 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
+"""
+
+from yams import xy
+
+xy.register_prefix('http://purl.org/dc/elements/1.1/', 'dc')
+xy.register_prefix('http://xmlns.com/foaf/0.1/', 'foaf')
+xy.register_prefix('http://usefulinc.com/ns/doap#', 'doap')
+
+xy.add_equivalence('creation_date', 'dc:date')
+xy.add_equivalence('created_by', 'dc:creator')
+xy.add_equivalence('description', 'dc:description')
+xy.add_equivalence('CWUser', 'foaf:Person')
+xy.add_equivalence('CWUser login', 'dc:title')
+xy.add_equivalence('CWUser surname', 'foaf:Person foaf:name')