backport stable branch
authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
Fri, 14 Aug 2009 00:02:08 +0200
changeset 2838 107421e426de
parent 2831 ddde6cf856a1 (diff)
parent 2837 65df5df190a5 (current diff)
child 2842 0477fff5f897
backport stable branch
server/serverctl.py
vregistry.py
--- a/__init__.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/__init__.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,7 +7,6 @@
 :license: Library General Public License version 2 - http://www.gnu.org/licenses
 """
 __docformat__ = "restructuredtext en"
-from cubicweb.__pkginfo__ import version as __version__
 
 import __builtin__
 # '_' is available in builtins to mark internationalized string but should
@@ -19,9 +18,7 @@
 
 import sys, os, logging
 from StringIO import StringIO
-from urllib import quote as urlquote, unquote as urlunquote
 
-from logilab.common.decorators import cached
 from logilab.common.logging_ext import set_log_methods
 
 if os.environ.get('APYCOT_ROOT'):
@@ -29,6 +26,8 @@
 else:
     logging.basicConfig()
 
+from cubicweb.__pkginfo__ import version as __version__
+
 
 set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
 
@@ -57,173 +56,6 @@
         StringIO.write(self, data)
 
 
-class RequestSessionMixIn(object):
-    """mixin class containing stuff shared by server session and web request
-    """
-    def __init__(self, vreg):
-        self.vreg = vreg
-        try:
-            encoding = vreg.property_value('ui.encoding')
-        except: # no vreg or property not registered
-            encoding = 'utf-8'
-        self.encoding = encoding
-        # cache result of execution for (rql expr / eids),
-        # should be emptied on commit/rollback of the server session / web
-        # connection
-        self.local_perm_cache = {}
-
-    def property_value(self, key):
-        if self.user:
-            return self.user.property_value(key)
-        return self.vreg.property_value(key)
-
-    def etype_rset(self, etype, size=1):
-        """return a fake result set for a particular entity type"""
-        from cubicweb.rset import ResultSet
-        rset = ResultSet([('A',)]*size, '%s X' % etype,
-                         description=[(etype,)]*size)
-        def get_entity(row, col=0, etype=etype, req=self, rset=rset):
-            return req.vreg.etype_class(etype)(req, rset, row, col)
-        rset.get_entity = get_entity
-        return self.decorate_rset(rset)
-
-    def eid_rset(self, eid, etype=None):
-        """return a result set for the given eid without doing actual query
-        (we have the eid, we can suppose it exists and user has access to the
-        entity)
-        """
-        from cubicweb.rset import ResultSet
-        eid = typed_eid(eid)
-        if etype is None:
-            etype = self.describe(eid)[0]
-        rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
-                         [(etype,)])
-        return self.decorate_rset(rset)
-
-    def empty_rset(self):
-        """return a result set for the given eid without doing actual query
-        (we have the eid, we can suppose it exists and user has access to the
-        entity)
-        """
-        from cubicweb.rset import ResultSet
-        return self.decorate_rset(ResultSet([], 'Any X WHERE X eid -1'))
-
-    def entity_from_eid(self, eid, etype=None):
-        try:
-            return self.entity_cache(eid)
-        except KeyError:
-            rset = self.eid_rset(eid, etype)
-            entity = rset.get_entity(0, 0)
-            self.set_entity_cache(entity)
-            return entity
-
-    def entity_cache(self, eid):
-        raise KeyError
-    def set_entity_cache(self, entity):
-        pass
-    # url generation methods ##################################################
-
-    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:
-            assert method == 'view', method
-            path = kwargs.pop('_restpath')
-        else:
-            path = method
-        if not kwargs:
-            return u'%s%s' % (base_url, path)
-        return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
-
-
-    def build_url_params(self, **kwargs):
-        """return encoded params to incorporate them in an URL"""
-        args = []
-        for param, values in kwargs.items():
-            if not isinstance(values, (list, tuple)):
-                values = (values,)
-            for value in values:
-                args.append(u'%s=%s' % (param, self.url_quote(value)))
-        return '&'.join(args)
-
-    def url_quote(self, value, safe=''):
-        """urllib.quote is not unicode safe, use this method to do the
-        necessary encoding / decoding. Also it's designed to quote each
-        part of a url path and so the '/' character will be encoded as well.
-        """
-        if isinstance(value, unicode):
-            quoted = urlquote(value.encode(self.encoding), safe=safe)
-            return unicode(quoted, self.encoding)
-        return urlquote(str(value), safe=safe)
-
-    def url_unquote(self, quoted):
-        """returns a unicode unquoted string
-
-        decoding is based on `self.encoding` which is the encoding
-        used in `url_quote`
-        """
-        if isinstance(quoted, unicode):
-            quoted = quoted.encode(self.encoding)
-        try:
-            return unicode(urlunquote(quoted), self.encoding)
-        except UnicodeDecodeError: # might occurs on manually typed URLs
-            return unicode(urlunquote(quoted), 'iso-8859-1')
-
-
-    # session's user related methods #####################################
-
-    @cached
-    def user_data(self):
-        """returns a dictionnary with this user's information"""
-        userinfo = {}
-        if self.is_internal_session:
-            userinfo['login'] = "cubicweb"
-            userinfo['name'] = "cubicweb"
-            userinfo['email'] = ""
-            return userinfo
-        user = self.actual_session().user
-        rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
-        try:
-            firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
-            if firstname is None and lastname is None:
-                userinfo['name'] = ''
-            else:
-                userinfo['name'] = ("%s %s" % (firstname, lastname))
-            userinfo['email'] = email
-        except IndexError:
-            userinfo['name'] = None
-            userinfo['email'] = None
-        userinfo['login'] = user.login
-        return userinfo
-
-    def is_internal_session(self):
-        """overrided on the server-side"""
-        return False
-
-    # abstract methods to override according to the web front-end #############
-
-    def base_url(self):
-        """return the root url of the instance"""
-        raise NotImplementedError
-
-    def decorate_rset(self, rset):
-        """add vreg/req (at least) attributes to the given result set """
-        raise NotImplementedError
-
-    def describe(self, eid):
-        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
-        raise NotImplementedError
-
-
 # XXX 2.45 is allowing nicer entity type names, use this map for bw compat
 ETYPE_NAME_MAP = {# 3.2 migration
                   'ECache': 'CWCache',
@@ -314,9 +146,6 @@
     except AttributeError:
         return neg_role(obj.role)
 
-def underline_title(title, car='-'):
-    return title+'\n'+(car*len(title))
-
 
 class CubicWebEventManager(object):
     """simple event / callback manager.
--- a/appobject.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/appobject.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,28 +11,12 @@
 
 import types
 from logging import getLogger
-from datetime import datetime, timedelta, time
 
 from logilab.common.decorators import classproperty
 from logilab.common.deprecation import deprecated
 from logilab.common.logging_ext import set_log_methods
 
-from rql.nodes import VariableRef, SubQuery
-from rql.stmts import Union, Select
-
 from cubicweb import Unauthorized, NoSelectableObject
-from cubicweb.utils import UStringIO, ustrftime, strptime, todate, todatetime
-
-ONESECOND = timedelta(0, 1, 0)
-CACHE_REGISTRY = {}
-
-
-class Cache(dict):
-    def __init__(self):
-        super(Cache, self).__init__()
-        _now = datetime.now()
-        self.cache_creation_date = _now
-        self.latest_cache_lookup = _now
 
 
 # selector base classes and operations ########################################
@@ -220,7 +204,7 @@
     :__registry__:
       name of the registry for this object (string like 'views',
       'templates'...)
-    :id:
+    :__id__:
       object's identifier in the registry (string like 'main',
       'primary', 'folder_box')
     :__select__:
@@ -229,105 +213,93 @@
     Moreover, the `__abstract__` attribute may be set to True to indicate
     that a appobject is abstract and should not be registered.
 
-    At registration time, the following attributes are set on the class:
-    :vreg:
-      the instance's registry
-    :schema:
-      the instance's schema
-    :config:
-      the instance's configuration
+    At selection time, the following attributes are set on the instance:
+
+    :cw_req:
+      current request
+    :cw_extra_kwargs:
+      other received arguments
 
-    At selection time, the following attributes are set on the instance:
-    :req:
-      current request
-    :rset:
+    only if rset is found in arguments (in which case rset/row/col will be
+    removed from cwextra_kwargs):
+
+    :cw_rset:
       context result set or None
-    :row:
+    :cw_row:
       if a result set is set and the context is about a particular cell in the
       result set, and not the result set as a whole, specify the row number we
       are interested in, else None
-    :col:
+    :cw_col:
       if a result set is set and the context is about a particular cell in the
       result set, and not the result set as a whole, specify the col number we
       are interested in, else None
     """
     __registry__ = None
-    id = None
+    __id__ = None
     __select__ = yes()
 
     @classmethod
-    def classid(cls):
-        """returns a unique identifier for the appobject"""
-        return '%s.%s' % (cls.__module__, cls.__name__)
-
-    # XXX bw compat code
-    @classmethod
-    def build___select__(cls):
-        for klass in cls.mro():
-            if klass.__name__ == 'AppObject':
-                continue # the bw compat __selector__ is there
-            klassdict = klass.__dict__
-            if ('__select__' in klassdict and '__selectors__' in klassdict
-                and '__selgenerated__' not in klassdict):
-                raise TypeError("__select__ and __selectors__ can't be used together on class %s" % cls)
-            if '__selectors__' in klassdict and '__selgenerated__' not in klassdict:
-                cls.__selgenerated__ = True
-                # case where __selectors__ is defined locally (but __select__
-                # is in a parent class)
-                selectors = klassdict['__selectors__']
-                if len(selectors) == 1:
-                    # micro optimization: don't bother with AndSelector if there's
-                    # only one selector
-                    select = _instantiate_selector(selectors[0])
-                else:
-                    select = AndSelector(*selectors)
-                cls.__select__ = select
-
-    @classmethod
-    def registered(cls, registry):
+    def __registered__(cls, registry):
         """called by the registry when the appobject has been registered.
 
         It must return the object that will be actually registered (this may be
         the right hook to create an instance for example). By default the
         appobject is returned without any transformation.
         """
-        cls.build___select__()
-        cls.vreg = registry.vreg
-        cls.schema = registry.schema
-        cls.config = registry.config
-        cls.register_properties()
+        pdefs = getattr(cls, 'cw_property_defs', {})
+        for propid, pdef in pdefs.items():
+            pdef = pdef.copy() # may be shared
+            pdef['default'] = getattr(cls, propid, pdef['default'])
+            pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
+            registry.vreg.register_property(cls._cwpropkey(propid), **pdef)
         return cls
 
-    @classmethod
-    def vreg_initialization_completed(cls):
-        pass
+    def __init__(self, req, **extra):
+        super(AppObject, self).__init__()
+        self.cw_req = req
+        try:
+            self.cw_rset = extra.pop('rset')
+            self.cw_row = extra.pop('row', None)
+            self.cw_col = extra.pop('col', None)
+        except KeyError:
+            pass
+        self.cw_extra_kwargs = extra
 
-    # Eproperties definition:
-    # key: id of the property (the actual CWProperty key is build using
-    #      <registry name>.<obj id>.<property id>
-    # value: tuple (property type, vocabfunc, default value, property description)
-    #        possible types are those used by `logilab.common.configuration`
+    # persistent class properties ##############################################
+    #
+    # optional `cw_property_defs` dict on a class defines available persistent
+    # properties for this class:
+    #
+    # * key: id of the property (the actual CWProperty key is build using
+    #        <registry name>.<obj id>.<property id>
+    # * value: tuple (property type, vocabfunc, default value, property description)
+    #         possible types are those used by `logilab.common.configuration`
     #
     # notice that when it exists multiple objects with the same id (adaptation,
     # overriding) only the first encountered definition is considered, so those
     # objects can't try to have different default values for instance.
-
-    property_defs = {}
+    #
+    # you can then access to a property value using self.cw_propval, where self
+    # is an instance of class
 
     @classmethod
-    def register_properties(cls):
-        for propid, pdef in cls.property_defs.items():
-            pdef = pdef.copy() # may be shared
-            pdef['default'] = getattr(cls, propid, pdef['default'])
-            pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
-            cls.vreg.register_property(cls.propkey(propid), **pdef)
-
-    @classmethod
-    def propkey(cls, propid):
+    def _cwpropkey(cls, propid):
+        """return cw property key for the property of the given id for this
+        class
+        """
         return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
 
+    def cw_propval(self, propid):
+        """return cw property value associated to key
+
+        <cls.__registry__>.<cls.id>.<propid>
+        """
+        return self.req.property_value(self._cwpropkey(propid))
+
+    # deprecated ###############################################################
+
     @classproperty
-    @deprecated('use __select__ and & or | operators')
+    @deprecated('[3.4] use __select__ and & or | operators')
     def __selectors__(cls):
         selector = cls.__select__
         if isinstance(selector, AndSelector):
@@ -336,236 +308,100 @@
             selector = (selector,)
         return selector
 
-    def __init__(self, req=None, rset=None, row=None, col=None, **extra):
-        super(AppObject, self).__init__()
-        self.req = req
-        self.rset = rset
-        self.row = row
-        self.col = col
-        self.extra_kwargs = extra
+    @property
+    @deprecated('[3.5] use cw_req.vreg')
+    def vreg(self):
+        return self.cw_req.vreg
+
+    @property
+    @deprecated('[3.5] use cw_req.vreg.schema')
+    def schema(self):
+        return self.cw_req.vreg.schema
+
+    @property
+    @deprecated('[3.5] use cw_req.vreg.config')
+    def config(self):
+        return self.cw_req.vreg.config
 
-    def get_cache(self, cachename):
-        """
-        NOTE: cachename should be dotted names as in :
-        - cubicweb.mycache
-        - cubes.blog.mycache
-        - etc.
-        """
-        if cachename in CACHE_REGISTRY:
-            cache = CACHE_REGISTRY[cachename]
-        else:
-            cache = CACHE_REGISTRY[cachename] = Cache()
-        _now = datetime.now()
-        if _now > cache.latest_cache_lookup + ONESECOND:
-            ecache = self.req.execute('Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T',
-                                      {'name':cachename}).get_entity(0,0)
-            cache.latest_cache_lookup = _now
-            if not ecache.valid(cache.cache_creation_date):
-                cache.clear()
-                cache.cache_creation_date = _now
-        return cache
+    @property
+    @deprecated('use self.cw_req')
+    def req(self):
+        return self.cw_req
 
-    def propval(self, propid):
-        assert self.req
-        return self.req.property_value(self.propkey(propid))
+    @property
+    @deprecated('use self.cw_rset')
+    def rset(self):
+        return self.cw_rset
 
-    def limited_rql(self):
-        """return a printable rql for the result set associated to the object,
-        with limit/offset correctly set according to maximum page size and
-        currently displayed page when necessary
-        """
-        # 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['components'].select_object('navigation', self.req,
-                                                    rset=self.rset)
-        if nav:
-            start, stop = nav.page_boundaries()
-            rql = self._limit_offset_rql(stop - start, start)
-        # result set may have be limited manually in which case navigation won't
-        # apply
-        elif self.rset.limited:
-            rql = self._limit_offset_rql(*self.rset.limited)
-        # navigation component doesn't apply and rset has not been limited, no
-        # need to limit query
-        else:
-            rql = self.rset.printable_rql()
-        return rql
+    @property
+    @deprecated('use self.cw_row')
+    def row(self):
+        return self.cw_row
 
-    def _limit_offset_rql(self, limit, offset):
-        rqlst = self.rset.syntax_tree()
-        if len(rqlst.children) == 1:
-            select = rqlst.children[0]
-            olimit, ooffset = select.limit, select.offset
-            select.limit, select.offset = limit, offset
-            rql = rqlst.as_string(kwargs=self.rset.args)
-            # restore original limit/offset
-            select.limit, select.offset = olimit, ooffset
-        else:
-            newselect = Select()
-            newselect.limit = limit
-            newselect.offset = offset
-            aliases = [VariableRef(newselect.get_variable(vref.name, i))
-                       for i, vref in enumerate(rqlst.selection)]
-            newselect.set_with([SubQuery(aliases, rqlst)], check=False)
-            newunion = Union()
-            newunion.append(newselect)
-            rql = rqlst.as_string(kwargs=self.rset.args)
-            rqlst.parent = None
-        return rql
+    @property
+    @deprecated('use self.cw_col')
+    def col(self):
+        return self.cw_col
 
-    def view(self, __vid, rset=None, __fallback_oid=None, __registry='views',
-             **kwargs):
-        """shortcut to self.vreg.view method avoiding to pass self.req"""
-        return self.vreg[__registry].render(__vid, self.req, __fallback_oid,
-                                            rset=rset, **kwargs)
+    @property
+    @deprecated('use self.cw_extra_kwargs')
+    def extra_kwargs(self):
+        return self.cw_extra_kwargs
 
+    @deprecated('[3.5] use cw_req.view')
+    def view(self, *args, **kwargs):
+        return self.cw_req.view(*args, **kwargs)
+
+    @deprecated('[3.5] use cw_req.varmaker')
     def initialize_varmaker(self):
-        varmaker = self.req.get_page_data('rql_varmaker')
-        if varmaker is None:
-            varmaker = self.req.varmaker
-            self.req.set_page_data('rql_varmaker', varmaker)
-        self.varmaker = varmaker
+        self.varmaker = self.cw_req.varmaker
 
-    # url generation methods ##################################################
+    @deprecated('[3.5] use cw_req.get_cache')
+    def get_cache(self, cachename):
+        return self.cw_req.get_cache(cachename)
 
-    controller = 'view'
-
+    @deprecated('[3.5] use cw_req.build_url')
     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:
-            method = self.controller
-            if method == 'view' and self.req.from_controller() == 'view' and \
-                   not '_restpath' in kwargs:
-                method = self.req.relative_path(includeparams=False) or 'view'
-        return self.req.build_url(method, **kwargs)
+        return self.cw_req.build_url(*args, **kwargs)
+
+    @deprecated('[3.5] use rset.limited_rql')
+    def limited_rql(self):
+        return self.rset.limited_rql()
 
-    # various resources accessors #############################################
+    @deprecated('[3.5] use self.rset.complete_entity(row,col) instead')
+    def complete_entity(self, row, col=0, skip_bytes=True):
+        return self.rset.complete_entity(row, col, skip_bytes)
 
+    @deprecated('[3.5] use self.rset.get_entity(row,col) instead')
     def entity(self, row, col=0):
-        """short cut to get an entity instance for a particular row/column
-        (col default to 0)
-        """
         return self.rset.get_entity(row, col)
 
-    def complete_entity(self, row, col=0, skip_bytes=True):
-        """short cut to get an completed entity instance for a particular
-        row (all instance's attributes have been fetched)
-        """
-        entity = self.entity(row, col)
-        entity.complete(skip_bytes=skip_bytes)
-        return entity
+    @deprecated('[3.5] use cw_req.user_rql_callback')
+    def user_rql_callback(self, args, msg=None):
+        return self.cw_req.user_rql_callback(args, msg)
 
-    def user_rql_callback(self, args, msg=None):
-        """register a user callback to execute some rql query and return an url
-        to call it ready to be inserted in html
-        """
-        def rqlexec(req, rql, args=None, key=None):
-            req.execute(rql, args, key)
-        return self.user_callback(rqlexec, args, msg)
-
+    @deprecated('[3.5] use cw_req.user_callback')
     def user_callback(self, cb, args, msg=None, nonify=False):
-        """register the given user callback and return an url to call it ready to be
-        inserted in html
-        """
-        from simplejson import dumps
-        self.req.add_js('cubicweb.ajax.js')
-        cbname = self.req.register_onetime_callback(cb, *args)
-        msg = dumps(msg or '')
-        return "javascript:userCallbackThenReloadPage('%s', %s)" % (
-            cbname, msg)
-
-    # formating methods #######################################################
+        return self.cw_req.user_callback(cb, args, msg, nonify)
 
-    def tal_render(self, template, variables):
-        """render a precompiled page template with variables in the given
-        dictionary as context
-        """
-        from cubicweb.ext.tal import CubicWebContext
-        context = CubicWebContext()
-        context.update({'self': self, 'rset': self.rset, '_' : self.req._,
-                        'req': self.req, 'user': self.req.user})
-        context.update(variables)
-        output = UStringIO()
-        template.expand(context, output)
-        return output.getvalue()
-
+    @deprecated('[3.5] use cw_req.format_date')
     def format_date(self, date, date_format=None, time=False):
-        """return a string for a date time according to instance's
-        configuration
-        """
-        if date:
-            if date_format is None:
-                if time:
-                    date_format = self.req.property_value('ui.datetime-format')
-                else:
-                    date_format = self.req.property_value('ui.date-format')
-            return ustrftime(date, date_format)
-        return u''
+        return self.cw_req.format_date(date, date_format, time)
 
+    @deprecated('[3.5] use cw_req.format_timoe')
     def format_time(self, time):
-        """return a string for a time according to instance's
-        configuration
-        """
-        if time:
-            return ustrftime(time, self.req.property_value('ui.time-format'))
-        return u''
+        return self.cw_req.format_time(time)
 
+    @deprecated('[3.5] use cw_req.format_float')
     def format_float(self, num):
-        """return a string for floating point number according to instance's
-        configuration
-        """
-        if num:
-            return self.req.property_value('ui.float-format') % num
-        return u''
+        return self.cw_req.format_float(num)
 
+    @deprecated('[3.5] use cw_req.parse_datetime')
     def parse_datetime(self, value, etype='Datetime'):
-        """get a datetime or time from a string (according to etype)
-        Datetime formatted as Date are accepted
-        """
-        assert etype in ('Datetime', 'Date', 'Time'), etype
-        # XXX raise proper validation error
-        if etype == 'Datetime':
-            format = self.req.property_value('ui.datetime-format')
-            try:
-                return todatetime(strptime(value, format))
-            except ValueError:
-                pass
-        elif etype == 'Time':
-            format = self.req.property_value('ui.time-format')
-            try:
-                # (adim) I can't find a way to parse a Time with a custom format
-                date = strptime(value, format) # this returns a DateTime
-                return time(date.hour, date.minute, date.second)
-            except ValueError:
-                raise ValueError('can\'t parse %r (expected %s)' % (value, format))
-        try:
-            format = self.req.property_value('ui.date-format')
-            dt = strptime(value, format)
-            if etype == 'Datetime':
-                return todatetime(dt)
-            return todate(dt)
-        except ValueError:
-            raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+        return self.cw_req.parse_datetime(value, etype)
 
-    # security related methods ################################################
-
-    def ensure_ro_rql(self, rql):
-        """raise an exception if the given rql is not a select query"""
-        first = rql.split(' ', 1)[0].lower()
-        if first in ('insert', 'set', 'delete'):
-            raise Unauthorized(self.req._('only select queries are authorized'))
+    @deprecated('[3.5] use self.cw_propval')
+    def propval(self, propid):
+        return self.cw_req.property_value(self._cwpropkey(propid))
 
 set_log_methods(AppObject, getLogger('cubicweb.appobject'))
--- a/common/mixins.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/mixins.py	Fri Aug 14 00:02:08 2009 +0200
@@ -299,7 +299,7 @@
     """handle an infinite recursion safety belt"""
     if done is None:
         done = set()
-    entity = view.entity(row, col)
+    entity = view.rset.get_entity(row, col)
     if entity.eid in done:
         msg = entity.req._('loop in %(rel)s relation (%(eid)s)') % {
             'rel': entity.tree_attribute,
--- a/common/mttransforms.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/mttransforms.py	Fri Aug 14 00:02:08 2009 +0200
@@ -15,6 +15,7 @@
                                  register_pil_transforms,
                                  register_pygments_transforms)
 
+from cubicweb.utils import UStringIO
 from cubicweb.common.uilib import rest_publish, html_publish
 
 HTML_MIMETYPES = ('text/html', 'text/xhtml', 'application/xhtml+xml')
@@ -43,7 +44,7 @@
 ENGINE.add_transform(html_to_html())
 
 try:
-    from cubicweb.ext.tal import compile_template
+    from cubicweb.ext.tal import CubicWebContext, compile_template
 except ImportError:
     HAS_TAL = False
     from cubicweb import schema
@@ -57,8 +58,16 @@
         output = 'text/html'
         output_encoding = 'utf-8'
         def _convert(self, trdata):
-            value = trdata.encode(self.output_encoding)
-            return trdata.appobject.tal_render(compile_template(value), {})
+            context = CubicWebContext()
+            appobject = trdata.appobject
+            context.update({'self': appobject, 'rset': appobject.rset,
+                            'req': appobject.req,
+                            '_' : appobject.req._,
+                            'user': appobject.req.user})
+            output = UStringIO()
+            template = compile_template(trdata.encode(self.output_encoding))
+            template.expand(context, output)
+            return output.getvalue()
 
     ENGINE.add_transform(ept_to_html())
 
--- a/common/test/unittest_mail.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/test/unittest_mail.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,7 +13,7 @@
 from logilab.common.testlib import unittest_main
 from logilab.common.umessage import message_from_string
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.common.mail import format_mail
 
 
@@ -25,7 +25,7 @@
     return pwd.getpwuid(os.getuid())[0]
 
 
-class EmailTC(EnvBasedTC):
+class EmailTC(CubicWebTC):
 
     def test_format_mail(self):
         self.set_option('sender-addr', 'bim@boum.fr')
--- a/common/test/unittest_migration.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/test/unittest_migration.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,8 +10,6 @@
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb.devtools import TestServerConfiguration
-from cubicweb.devtools.apptest import TestEnvironment
-
 from cubicweb.cwconfig import CubicWebConfiguration
 from cubicweb.common.migration import MigrationHelper, filter_scripts
 from cubicweb.server.migractions import ServerMigrationHelper
@@ -98,8 +96,8 @@
         config = ApptestConfiguration('data')
         source = config.sources()['system']
         self.assertEquals(source['db-driver'], 'sqlite')
-        cleanup_sqlite(source['db-name'], removecube=True)
-        init_test_database(driver=source['db-driver'], config=config)
+        cleanup_sqlite(source['db-name'], removetemplate=True)
+        init_test_database(config=config)
 
 
 if __name__ == '__main__':
--- a/common/test/unittest_mixins.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/test/unittest_mixins.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,9 +6,9 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
-class WorkfloableMixInTC(EnvBasedTC):
+class WorkfloableMixInTC(CubicWebTC):
     def test_wf_state(self):
         s = self.add_entity('State', name=u'activated')
         self.execute('SET X state_of ET WHERE ET name "Bookmark", X eid %(x)s',
--- a/common/uilib.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/common/uilib.py	Fri Aug 14 00:02:08 2009 +0200
@@ -12,7 +12,6 @@
 
 import csv
 import re
-from urllib import quote as urlquote
 from StringIO import StringIO
 
 from logilab.mtconverter import xml_escape, html_unescape
--- a/cwctl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/cwctl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,9 +11,10 @@
 from logilab.common.clcommands import register_commands, pop_arg
 from logilab.common.shellutils import ASK
 
-from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage, underline_title
+from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage
 from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS
-from cubicweb.toolsutils import Command, main_run,  rm, create_dir
+from cubicweb.toolsutils import Command, main_run, rm, create_dir, underline_title
+
 
 def wait_process_end(pid, maxtry=10, waittime=1):
     """wait for a process to actually die"""
--- a/cwvreg.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/cwvreg.py	Fri Aug 14 00:02:08 2009 +0200
@@ -18,7 +18,7 @@
                       ObjectNotFound, NoSelectableObject, RegistryNotFound,
                       RegistryOutOfDate, CW_EVENT_MANAGER)
 from cubicweb.utils import dump_class
-from cubicweb.vregistry import VRegistry, Registry
+from cubicweb.vregistry import VRegistry, Registry, class_regid
 from cubicweb.rtags import RTAGS
 
 
@@ -50,12 +50,9 @@
         self.schema = vreg.schema
 
     def initialization_completed(self):
-        # call vreg_initialization_completed on appobjects and print
-        # registry content
-        for appobjects in self.itervalues():
-            for appobject in appobjects:
-                appobject.vreg_initialization_completed()
+        pass
 
+    @deprecated('[3.5] select object, then use obj.render()')
     def render(self, __oid, req, __fallback_oid=None, rset=None, **kwargs):
         """select object, or fallback object if specified and the first one
         isn't selectable, then render it
@@ -68,20 +65,22 @@
             obj = self.select(__fallback_oid, req, **kwargs)
         return obj.render(**kwargs)
 
+    @deprecated('[3.5] use select_or_none and test for obj.cw_propval("visible")')
     def select_vobject(self, oid, *args, **kwargs):
-        selected = self.select_object(oid, *args, **kwargs)
-        if selected and selected.propval('visible'):
+        selected = self.select_or_none(oid, *args, **kwargs)
+        if selected and selected.cw_propval('visible'):
             return selected
         return None
 
-    def possible_vobjects(self, *args, **kwargs):
+    def poss_visible_objects(self, *args, **kwargs):
         """return an ordered list of possible app objects in a given registry,
         supposing they support the 'visible' and 'order' properties (as most
         visualizable objects)
         """
         return sorted([x for x in self.possible_objects(*args, **kwargs)
-                       if x.propval('visible')],
-                      key=lambda x: x.propval('order'))
+                       if x.cw_propval('visible')],
+                      key=lambda x: x.cw_propval('order'))
+    possible_vobjects = deprecated('[3.5] use poss_visible_objects()')(poss_visible_objects)
 
 
 VRegistry.REGISTRY_FACTORY[None] = CWRegistry
@@ -97,15 +96,22 @@
         clear_cache(self, 'etype_class')
 
     def register(self, obj, **kwargs):
-        oid = kwargs.get('oid') or obj.id
+        oid = kwargs.get('oid') or class_regid(obj)
         if oid != 'Any' and not oid in self.schema:
             self.error('don\'t register %s, %s type not defined in the '
-                       'schema', obj, obj.id)
+                       'schema', obj, oid)
             return
         kwargs['clear'] = True
         super(ETypeRegistry, self).register(obj, **kwargs)
 
     @cached
+    def parent_classes(self, etype):
+        eschema = self.schema.eschema(etype)
+        parents = [cls.etype_class(e.type) for e in eschema.ancestors()]
+        parents.append(self.etype_class('Any'))
+        return parents
+
+    @cached
     def etype_class(self, etype):
         """return an entity class for the given entity type.
 
@@ -139,12 +145,12 @@
             objects = self['Any']
             assert len(objects) == 1, objects
             cls = objects[0]
-        if cls.id == etype:
-            cls.__initialize__()
+        if cls.__id__ == etype:
+            cls.__initialize__(self.schema)
             return cls
         cls = dump_class(cls, etype)
-        cls.id = etype
-        cls.__initialize__()
+        cls.__id__ = etype
+        cls.__initialize__(self.schema)
         return cls
 
 VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry
@@ -172,7 +178,7 @@
             if vid[0] == '_':
                 continue
             try:
-                view = self.select_best(views, req, rset=rset, **kwargs)
+                view = self._select_best(views, req, rset=rset, **kwargs)
                 if view.linkable():
                     yield view
             except NoSelectableObject:
@@ -351,6 +357,8 @@
             implemented_interfaces = set()
             if 'Any' in self.get('etypes', ()):
                 for etype in self.schema.entities():
+                    if etype.is_final():
+                        continue
                     cls = self['etypes'].etype_class(etype)
                     for iface in cls.__implements__:
                         implemented_interfaces.update(iface.__mro__)
@@ -387,35 +395,35 @@
                          special_relations={'eid': 'uid', 'has_text': 'fti'})
 
 
-    @deprecated('use vreg["etypes"].etype_class(etype)')
+    @deprecated('[3.4] use vreg["etypes"].etype_class(etype)')
     def etype_class(self, etype):
         return self["etypes"].etype_class(etype)
 
-    @deprecated('use vreg["views"].main_template(*args, **kwargs)')
+    @deprecated('[3.4] use vreg["views"].main_template(*args, **kwargs)')
     def main_template(self, req, oid='main-template', **context):
         return self["views"].main_template(req, oid, **context)
 
-    @deprecated('use vreg[registry].possible_vobjects(*args, **kwargs)')
+    @deprecated('[3.4] use vreg[registry].possible_vobjects(*args, **kwargs)')
     def possible_vobjects(self, registry, *args, **kwargs):
         return self[registry].possible_vobjects(*args, **kwargs)
 
-    @deprecated('use vreg["actions"].possible_actions(*args, **kwargs)')
+    @deprecated('[3.4] use vreg["actions"].possible_actions(*args, **kwargs)')
     def possible_actions(self, req, rset=None, **kwargs):
         return self["actions"].possible_actions(req, rest=rset, **kwargs)
 
-    @deprecated("use vreg['boxes'].select_object(...)")
+    @deprecated("[3.4] use vreg['boxes'].select_or_none(...)")
     def select_box(self, oid, *args, **kwargs):
-        return self['boxes'].select_object(oid, *args, **kwargs)
+        return self['boxes'].select_or_none(oid, *args, **kwargs)
 
-    @deprecated("use vreg['components'].select_object(...)")
+    @deprecated("[3.4] use vreg['components'].select_or_none(...)")
     def select_component(self, cid, *args, **kwargs):
-        return self['components'].select_object(cid, *args, **kwargs)
+        return self['components'].select_or_none(cid, *args, **kwargs)
 
-    @deprecated("use vreg['actions'].select_object(...)")
+    @deprecated("[3.4] use vreg['actions'].select_or_none(...)")
     def select_action(self, oid, *args, **kwargs):
-        return self['actions'].select_object(oid, *args, **kwargs)
+        return self['actions'].select_or_none(oid, *args, **kwargs)
 
-    @deprecated("use vreg['views'].select(...)")
+    @deprecated("[3.4] use vreg['views'].select(...)")
     def select_view(self, __vid, req, rset=None, **kwargs):
         return self['views'].select(__vid, req, rset=rset, **kwargs)
 
--- a/dbapi.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/dbapi.py	Fri Aug 14 00:02:08 2009 +0200
@@ -19,8 +19,9 @@
 from logilab.common.decorators import monkeypatch
 from logilab.common.deprecation import deprecated
 
-from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
-from cubicweb import cwvreg, cwconfig
+from cubicweb import ETYPE_NAME_MAP, ConnectionError, cwvreg, cwconfig
+from cubicweb.req import RequestSessionBase
+
 
 _MARKER = object()
 
@@ -42,9 +43,9 @@
     registries.
     """
     defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
-    orig_select_best = defaultcls.orig_select_best = defaultcls.select_best
+    orig_select_best = defaultcls.orig_select_best = defaultcls._select_best
     @monkeypatch(defaultcls)
-    def select_best(self, appobjects, *args, **kwargs):
+    def _select_best(self, appobjects, *args, **kwargs):
         """return an instance of the most specific object according
         to parameters
 
@@ -174,7 +175,7 @@
     return repo, cnx
 
 
-class DBAPIRequest(RequestSessionMixIn):
+class DBAPIRequest(RequestSessionBase):
 
     def __init__(self, vreg, cnx=None):
         super(DBAPIRequest, self).__init__(vreg)
--- a/devtools/__init__.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/__init__.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,20 +13,54 @@
 from os.path import (abspath, join, exists, basename, dirname, normpath, split,
                      isfile, isabs)
 
-from cubicweb import CW_SOFTWARE_ROOT, ConfigurationError
+from cubicweb import CW_SOFTWARE_ROOT, ConfigurationError, schema, cwconfig
 from cubicweb.utils import strptime
-from cubicweb.toolsutils import read_config
-from cubicweb.cwconfig import CubicWebConfiguration, merge_options
 from cubicweb.server.serverconfig import ServerConfiguration
 from cubicweb.etwist.twconfig import TwistedConfiguration
 
+cwconfig.CubicWebConfiguration.cls_adjust_sys_path()
+
+# db auto-population configuration #############################################
+
+SYSTEM_ENTITIES = schema.SCHEMA_TYPES | set((
+    'CWGroup', 'CWUser', 'CWProperty',
+    'State', 'Transition', 'TrInfo',
+    ))
+
+SYSTEM_RELATIONS = schema.META_RTYPES | set((
+    # workflow related
+    'state_of', 'transition_of', 'initial_state', 'allowed_transition',
+    'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state',
+    'condition',
+    # cwproperty
+    'for_user',
+    # schema definition
+    'specializes',
+    'relation_type', 'from_entity', 'to_entity',
+    'constrained_by', 'cstrtype', 'widget',
+    'read_permission', 'update_permission', 'delete_permission', 'add_permission',
+    # permission
+    'in_group', 'require_group', 'require_permission',
+    # deducted from other relations
+    'primary_email',
+    ))
+
+# content validation configuration #############################################
+
 # validators are used to validate (XML, DTD, whatever) view's content
 # validators availables are :
 #  'dtd' : validates XML + declared DTD
 #  'xml' : guarantees XML is well formed
 #  None : do not try to validate anything
+
+# {'vid': validator}
 VIEW_VALIDATORS = {}
+
+
+# cubicweb test configuration ##################################################
+
 BASE_URL = 'http://testing.fr/cubicweb/'
+
 DEFAULT_SOURCES = {'system': {'adapter' : 'native',
                               'db-encoding' : 'UTF-8', #'ISO-8859-1',
                               'db-user' : u'admin',
@@ -40,13 +74,14 @@
                               },
                    }
 
+
 class TestServerConfiguration(ServerConfiguration):
     mode = 'test'
     set_language = False
     read_instance_schema = False
     bootstrap_schema = False
     init_repository = True
-    options = merge_options(ServerConfiguration.options + (
+    options = cwconfig.merge_options(ServerConfiguration.options + (
         ('anonymous-user',
          {'type' : 'string',
           'default': None,
@@ -66,7 +101,6 @@
 
     def __init__(self, appid, log_threshold=logging.CRITICAL+10):
         ServerConfiguration.__init__(self, appid)
-        self.global_set_option('log-file', None)
         self.init_log(log_threshold, force=True)
         # need this, usually triggered by cubicweb-ctl
         self.load_cwctl_plugins()
@@ -118,30 +152,11 @@
             sources = DEFAULT_SOURCES
         return sources
 
-    def load_defaults(self):
-        super(TestServerConfiguration, self).load_defaults()
-        # note: don't call global set option here, OptionManager may not yet be initialized
-        # add anonymous user
-        self.set_option('anonymous-user', 'anon')
-        self.set_option('anonymous-password', 'anon')
-        # uncomment the line below if you want rql queries to be logged
-        #self.set_option('query-log-file', '/tmp/test_rql_log.' + `os.getpid()`)
-        self.set_option('sender-name', 'cubicweb-test')
-        self.set_option('sender-addr', 'cubicweb-test@logilab.fr')
-        try:
-            send_to =  '%s@logilab.fr' % os.getlogin()
-        except OSError:
-            send_to =  '%s@logilab.fr' % (os.environ.get('USER')
-                                          or os.environ.get('USERNAME')
-                                          or os.environ.get('LOGNAME'))
-        self.set_option('sender-addr', send_to)
-        self.set_option('default-dest-addrs', send_to)
-        self.set_option('base-url', BASE_URL)
-
 
 class BaseApptestConfiguration(TestServerConfiguration, TwistedConfiguration):
     repo_method = 'inmemory'
-    options = merge_options(TestServerConfiguration.options + TwistedConfiguration.options)
+    options = cwconfig.merge_options(TestServerConfiguration.options
+                                     + TwistedConfiguration.options)
     cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | TwistedConfiguration.cubicweb_appobject_path
     cube_appobject_path = TestServerConfiguration.cube_appobject_path | TwistedConfiguration.cube_appobject_path
 
@@ -163,98 +178,91 @@
         BaseApptestConfiguration.__init__(self, appid, log_threshold=log_threshold)
         self.init_repository = sourcefile is None
         self.sourcefile = sourcefile
-        import re
-        self.global_set_option('embed-allowed', re.compile('.*'))
+        self.global_set_option('anonymous-user', 'anon')
+        self.global_set_option('anonymous-password', 'anon')
+
+    def load_configuration(self):
+        super(ApptestConfiguration, self).load_configuration()
+        self.global_set_option('anonymous-user', 'anon')
+        self.global_set_option('anonymous-password', 'anon')
 
 
-class RealDatabaseConfiguration(ApptestConfiguration):
-    init_repository = False
-    sourcesdef =  {'system': {'adapter' : 'native',
-                              'db-encoding' : 'UTF-8', #'ISO-8859-1',
-                              'db-user' : u'admin',
-                              'db-password' : 'gingkow',
-                              'db-name' : 'seotest',
-                              'db-driver' : 'postgres',
-                              'db-host' : None,
-                              },
-                   'admin' : {'login': u'admin',
-                              'password': u'gingkow',
-                              },
-                   }
+# test database handling #######################################################
 
-    def __init__(self, appid, log_threshold=logging.CRITICAL, sourcefile=None):
-        ApptestConfiguration.__init__(self, appid)
-        self.init_repository = False
+def init_test_database(config=None, configdir='data'):
+    """init a test database for a specific driver"""
+    from cubicweb.dbapi import in_memory_cnx
+    config = config or TestServerConfiguration(configdir)
+    sources = config.sources()
+    driver = sources['system']['db-driver']
+    if driver == 'sqlite':
+        init_test_database_sqlite(config)
+    elif driver == 'postgres':
+        init_test_database_postgres(config)
+    else:
+        raise ValueError('no initialization function for driver %r' % driver)
+    config._cubes = None # avoid assertion error
+    repo, cnx = in_memory_cnx(config, unicode(sources['admin']['login']),
+                              sources['admin']['password'] or 'xxx')
+    if driver == 'sqlite':
+        install_sqlite_patch(repo.querier)
+    return repo, cnx
 
 
-    def sources(self):
-        """
-        By default, we run tests with the sqlite DB backend.
-        One may use its own configuration by just creating a
-        'sources' file in the test directory from wich tests are
-        launched.
-        """
-        self._sources = self.sourcesdef
-        return self._sources
+def reset_test_database(config):
+    """init a test database for a specific driver"""
+    driver = config.sources()['system']['db-driver']
+    if driver == 'sqlite':
+        reset_test_database_sqlite(config)
+    else:
+        raise ValueError('no reset function for driver %r' % driver)
 
 
-def buildconfig(dbuser, dbpassword, dbname, adminuser, adminpassword, dbhost=None):
-    """convenience function that builds a real-db configuration class"""
-    sourcesdef =  {'system': {'adapter' : 'native',
-                              'db-encoding' : 'UTF-8', #'ISO-8859-1',
-                              'db-user' : dbuser,
-                              'db-password' : dbpassword,
-                              'db-name' : dbname,
-                              'db-driver' : 'postgres',
-                              'db-host' : dbhost,
-                              },
-                   'admin' : {'login': adminuser,
-                              'password': adminpassword,
-                              },
-                   }
-    return type('MyRealDBConfig', (RealDatabaseConfiguration,),
-                {'sourcesdef': sourcesdef})
+### postgres test database handling ############################################
 
-def loadconfig(filename):
-    """convenience function that builds a real-db configuration class
-    from a file
-    """
-    return type('MyRealDBConfig', (RealDatabaseConfiguration,),
-                {'sourcesdef': read_config(filename)})
+def init_test_database_postgres(config):
+    """initialize a fresh sqlite databse used for testing purpose"""
+    if config.init_repository:
+        from cubicweb.server import init_repository
+        init_repository(config, interactive=False, drop=True)
 
 
-class LivetestConfiguration(BaseApptestConfiguration):
-    init_repository = False
+### sqlite test database handling ##############################################
+
+def cleanup_sqlite(dbfile, removetemplate=False):
+    try:
+        os.remove(dbfile)
+        os.remove('%s-journal' % dbfile)
+    except OSError:
+        pass
+    if removetemplate:
+        try:
+            os.remove('%s-template' % dbfile)
+        except OSError:
+            pass
 
-    def __init__(self, cube=None, sourcefile=None, pyro_name=None,
-                 log_threshold=logging.CRITICAL):
-        TestServerConfiguration.__init__(self, cube, log_threshold=log_threshold)
-        self.appid = pyro_name or cube
-        # don't change this, else some symlink problems may arise in some
-        # environment (e.g. mine (syt) ;o)
-        # XXX I'm afraid this test will prevent to run test from a production
-        # environment
-        self._sources = None
-        # 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:
-            # cube test
-            self.apphome = abspath('..')
-        self.sourcefile = sourcefile
-        self.global_set_option('realm', '')
-        self.use_pyro = pyro_name is not None
+def reset_test_database_sqlite(config):
+    import shutil
+    dbfile = config.sources()['system']['db-name']
+    cleanup_sqlite(dbfile)
+    template = '%s-template' % dbfile
+    if exists(template):
+        shutil.copy(template, dbfile)
+        return True
+    return False
 
-    def pyro_enabled(self):
-        if self.use_pyro:
-            return True
-        else:
-            return False
+def init_test_database_sqlite(config):
+    """initialize a fresh sqlite databse used for testing purpose"""
+    # remove database file if it exists
+    dbfile = config.sources()['system']['db-name']
+    if not reset_test_database_sqlite(config):
+        # initialize the database
+        import shutil
+        from cubicweb.server import init_repository
+        init_repository(config, interactive=False)
+        dbfile = config.sources()['system']['db-name']
+        shutil.copy(dbfile, '%s-template' % dbfile)
 
-CubicWebConfiguration.cls_adjust_sys_path()
 
 def install_sqlite_patch(querier):
     """This patch hotfixes the following sqlite bug :
@@ -293,57 +301,3 @@
         return new_execute
     querier.__class__.execute = wrap_execute(querier.__class__.execute)
     querier.__class__._devtools_sqlite_patched = True
-
-def init_test_database(driver='sqlite', configdir='data', config=None,
-                       vreg=None):
-    """init a test database for a specific driver"""
-    from cubicweb.dbapi import in_memory_cnx
-    if vreg and not config:
-        config = vreg.config
-    config = config or TestServerConfiguration(configdir)
-    source = config.sources()
-    if driver == 'sqlite':
-        init_test_database_sqlite(config, source)
-    elif driver == 'postgres':
-        init_test_database_postgres(config, source)
-    else:
-        raise ValueError('no initialization function for driver %r' % driver)
-    config._cubes = None # avoid assertion error
-    repo, cnx = in_memory_cnx(vreg or config, unicode(source['admin']['login']),
-                              source['admin']['password'] or 'xxx')
-    if driver == 'sqlite':
-        install_sqlite_patch(repo.querier)
-    return repo, cnx
-
-def init_test_database_postgres(config, source, vreg=None):
-    """initialize a fresh sqlite databse used for testing purpose"""
-    if config.init_repository:
-        from cubicweb.server import init_repository
-        init_repository(config, interactive=False, drop=True, vreg=vreg)
-
-def cleanup_sqlite(dbfile, removecube=False):
-    try:
-        os.remove(dbfile)
-        os.remove('%s-journal' % dbfile)
-    except OSError:
-        pass
-    if removecube:
-        try:
-            os.remove('%s-template' % dbfile)
-        except OSError:
-            pass
-
-def init_test_database_sqlite(config, source, vreg=None):
-    """initialize a fresh sqlite databse used for testing purpose"""
-    import shutil
-    # remove database file if it exists (actually I know driver == 'sqlite' :)
-    dbfile = source['system']['db-name']
-    cleanup_sqlite(dbfile)
-    template = '%s-template' % dbfile
-    if exists(template):
-        shutil.copy(template, dbfile)
-    else:
-        # initialize the database
-        from cubicweb.server import init_repository
-        init_repository(config, interactive=False, vreg=vreg)
-        shutil.copy(dbfile, template)
--- a/devtools/_apptest.py	Fri Aug 14 00:01:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-"""Hidden internals for the devtools.apptest module
-
-: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"
-
-import sys, traceback
-
-from logilab.common.pytest import pause_tracing, resume_tracing
-
-import yams.schema
-
-from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError
-from cubicweb.cwvreg import CubicWebVRegistry
-
-from cubicweb.web.application import CubicWebPublisher
-from cubicweb.web import Redirect
-
-from cubicweb.devtools import ApptestConfiguration, init_test_database
-from cubicweb.devtools.fake import FakeRequest
-
-SYSTEM_ENTITIES = ('CWGroup', 'CWUser',
-                   'CWAttribute', 'CWRelation',
-                   'CWConstraint', 'CWConstraintType', 'CWProperty',
-                   'CWEType', 'CWRType',
-                   'State', 'Transition', 'TrInfo',
-                   'RQLExpression',
-                   )
-SYSTEM_RELATIONS = (
-    # virtual relation
-    'identity',
-    # metadata
-    'is', 'is_instance_of', 'owned_by', 'created_by', 'specializes',
-    # workflow related
-    'state_of', 'transition_of', 'initial_state', 'allowed_transition',
-    'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state',
-    'condition',
-    # permission
-    'in_group', 'require_group', 'require_permission',
-    'read_permission', 'update_permission', 'delete_permission', 'add_permission',
-    # eproperty
-    'for_user',
-    # schema definition
-    'relation_type', 'from_entity', 'to_entity',
-    'constrained_by', 'cstrtype', 'widget',
-    # deducted from other relations
-    'primary_email',
-                    )
-
-def unprotected_entities(app_schema, strict=False):
-    """returned a Set of each non final entity type, excluding CWGroup, and CWUser...
-    """
-    if strict:
-        protected_entities = yams.schema.BASE_TYPES
-    else:
-        protected_entities = yams.schema.BASE_TYPES.union(set(SYSTEM_ENTITIES))
-    entities = set(app_schema.entities())
-    return entities - protected_entities
-
-
-def ignore_relations(*relations):
-    global SYSTEM_RELATIONS
-    SYSTEM_RELATIONS += relations
-
-
-class TestEnvironment(object):
-    """TestEnvironment defines a context (e.g. a config + a given connection) in
-    which the tests are executed
-    """
-
-    def __init__(self, appid, reporter=None, verbose=False,
-                 configcls=ApptestConfiguration, requestcls=FakeRequest):
-        config = configcls(appid)
-        self.requestcls = requestcls
-        self.cnx = None
-        config.db_perms = False
-        source = config.sources()['system']
-        if verbose:
-            print "init test database ..."
-        self.vreg = vreg = CubicWebVRegistry(config)
-        self.admlogin = source['db-user']
-        # restore database <=> init database
-        self.restore_database()
-        if verbose:
-            print "init done"
-        config.repository = lambda x=None: self.repo
-        self.app = CubicWebPublisher(config, vreg=vreg)
-        self.verbose = verbose
-        schema = self.vreg.schema
-        # else we may run into problems since email address are ususally share in app tests
-        # XXX should not be necessary anymore
-        schema.rschema('primary_email').set_rproperty('CWUser', 'EmailAddress', 'composite', False)
-        self.deletable_entities = unprotected_entities(schema)
-
-    def restore_database(self):
-        """called by unittests' tearDown to restore the original database
-        """
-        try:
-            pause_tracing()
-            if self.cnx:
-                self.cnx.close()
-            source = self.vreg.config.sources()['system']
-            self.repo, self.cnx = init_test_database(driver=source['db-driver'],
-                                                     vreg=self.vreg)
-            self._orig_cnx = self.cnx
-            resume_tracing()
-        except:
-            resume_tracing()
-            traceback.print_exc()
-            sys.exit(1)
-        # XXX cnx decoration is usually done by the repository authentication manager,
-        # necessary in authentication tests
-        self.cnx.vreg = self.vreg
-        self.cnx.login = source['db-user']
-        self.cnx.password = source['db-password']
-
-
-    def create_user(self, login, groups=('users',), req=None):
-        req = req or self.create_request()
-        cursor = self._orig_cnx.cursor(req)
-        rset = cursor.execute('INSERT CWUser X: X login %(login)s, X upassword %(passwd)s,'
-                              'X in_state S WHERE S name "activated"',
-                              {'login': unicode(login), 'passwd': login.encode('utf8')})
-        user = rset.get_entity(0, 0)
-        cursor.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
-                       % ','.join(repr(g) for g in groups),
-                       {'x': user.eid}, 'x')
-        user.clear_related_cache('in_group', 'subject')
-        self._orig_cnx.commit()
-        return user
-
-    def login(self, login, password=None):
-        if login == self.admlogin:
-            self.restore_connection()
-        else:
-            self.cnx = repo_connect(self.repo, unicode(login),
-                                    password or str(login),
-                                    ConnectionProperties('inmemory'))
-        if login == self.vreg.config.anonymous_user()[0]:
-            self.cnx.anonymous_connection = True
-        return self.cnx
-
-    def restore_connection(self):
-        if not self.cnx is self._orig_cnx:
-            try:
-                self.cnx.close()
-            except ProgrammingError:
-                pass # already closed
-        self.cnx = self._orig_cnx
-
-    ############################################################################
-
-    def execute(self, rql, args=None, eidkey=None, req=None):
-        """executes <rql>, builds a resultset, and returns a couple (rset, req)
-        where req is a FakeRequest
-        """
-        req = req or self.create_request(rql=rql)
-        return self.cnx.cursor(req).execute(unicode(rql), args, eidkey)
-
-    def create_request(self, rql=None, **kwargs):
-        """executes <rql>, builds a resultset, and returns a
-        couple (rset, req) where req is a FakeRequest
-        """
-        if rql:
-            kwargs['rql'] = rql
-        req = self.requestcls(self.vreg, form=kwargs)
-        req.set_connection(self.cnx)
-        return req
-
-    def get_rset_and_req(self, rql, optional_args=None, args=None, eidkey=None):
-        """executes <rql>, builds a resultset, and returns a
-        couple (rset, req) where req is a FakeRequest
-        """
-        return (self.execute(rql, args, eidkey),
-                self.create_request(rql=rql, **optional_args or {}))
-
-
-class ExistingTestEnvironment(TestEnvironment):
-
-    def __init__(self, appid, sourcefile, verbose=False):
-        config = ApptestConfiguration(appid, sourcefile=sourcefile)
-        if verbose:
-            print "init test database ..."
-        source = config.sources()['system']
-        self.vreg = CubicWebVRegistry(config)
-        self.cnx = init_test_database(driver=source['db-driver'],
-                                      vreg=self.vreg)[1]
-        if verbose:
-            print "init done"
-        self.app = CubicWebPublisher(config, vreg=self.vreg)
-        self.verbose = verbose
-        # this is done when the publisher is opening a connection
-        self.cnx.vreg = self.vreg
-
-    def setup(self, config=None):
-        """config is passed by TestSuite but is ignored in this environment"""
-        cursor = self.cnx.cursor()
-        self.last_eid = cursor.execute('Any X WHERE X creation_date D ORDERBY D DESC LIMIT 1').rows[0][0]
-
-    def cleanup(self):
-        """cancel inserted elements during tests"""
-        cursor = self.cnx.cursor()
-        cursor.execute('DELETE Any X WHERE X eid > %(x)s', {'x' : self.last_eid}, eid_key='x')
-        print "cleaning done"
-        self.cnx.commit()
--- a/devtools/apptest.py	Fri Aug 14 00:01:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,506 +0,0 @@
-"""This module provides misc utilities to test instances
-
-: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"
-
-from copy import deepcopy
-
-import simplejson
-
-from logilab.common.testlib import TestCase
-from logilab.common.pytest import nocoverage
-from logilab.common.umessage import message_from_string
-
-from logilab.common.deprecation import deprecated
-
-from cubicweb.devtools import init_test_database, TestServerConfiguration, ApptestConfiguration
-from cubicweb.devtools._apptest import TestEnvironment
-from cubicweb.devtools.fake import FakeRequest
-
-from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError
-
-
-MAILBOX = []
-class Email:
-    def __init__(self, recipients, msg):
-        self.recipients = recipients
-        self.msg = msg
-
-    @property
-    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'))
-
-class MockSMTP:
-    def __init__(self, server, port):
-        pass
-    def close(self):
-        pass
-    def sendmail(self, helo_addr, recipients, msg):
-        MAILBOX.append(Email(recipients, msg))
-
-from cubicweb import cwconfig
-cwconfig.SMTP = MockSMTP
-
-
-def get_versions(self, checkversions=False):
-    """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.
-
-    replace Repository.get_versions by this method if you don't want versions
-    checking
-    """
-    vcconf = {'cubicweb': self.config.cubicweb_version()}
-    self.config.bootstrap_cubes()
-    for pk in self.config.cubes():
-        version = self.config.cube_version(pk)
-        vcconf[pk] = version
-    self.config._cubes = None
-    return vcconf
-
-
-@property
-def late_binding_env(self):
-    """builds TestEnvironment as late as possible"""
-    if not hasattr(self, '_env'):
-        self.__class__._env = TestEnvironment('data', configcls=self.configcls,
-                                              requestcls=self.requestcls)
-    return self._env
-
-
-class autoenv(type):
-    """automatically set environment on EnvBasedTC subclasses if necessary
-    """
-    def __new__(mcs, name, bases, classdict):
-        env = classdict.get('env')
-        # try to find env in one of the base classes
-        if env is None:
-            for base in bases:
-                env = getattr(base, 'env', None)
-                if env is not None:
-                    classdict['env'] = env
-                    break
-        if not classdict.get('__abstract__')  and not classdict.get('env'):
-            classdict['env'] = late_binding_env
-        return super(autoenv, mcs).__new__(mcs, name, bases, classdict)
-
-
-class EnvBasedTC(TestCase):
-    """abstract class for test using an apptest environment
-    """
-    __metaclass__ = autoenv
-    __abstract__ = True
-    env = None
-    configcls = ApptestConfiguration
-    requestcls = FakeRequest
-
-    # user / session management ###############################################
-
-    def user(self, req=None):
-        if req is None:
-            req = self.env.create_request()
-            return self.env.cnx.user(req)
-        else:
-            return req.user
-
-    def create_user(self, *args, **kwargs):
-        return self.env.create_user(*args, **kwargs)
-
-    def login(self, login, password=None):
-        return self.env.login(login, password)
-
-    def restore_connection(self):
-        self.env.restore_connection()
-
-    # db api ##################################################################
-
-    @nocoverage
-    def cursor(self, req=None):
-        return self.env.cnx.cursor(req or self.request())
-
-    @nocoverage
-    def execute(self, *args, **kwargs):
-        return self.env.execute(*args, **kwargs)
-
-    @nocoverage
-    def commit(self):
-        self.env.cnx.commit()
-
-    @nocoverage
-    def rollback(self):
-        try:
-            self.env.cnx.rollback()
-        except ProgrammingError:
-            pass
-
-    # other utilities #########################################################
-    def set_debug(self, debugmode):
-        from cubicweb.server import set_debug
-        set_debug(debugmode)
-
-    @property
-    def config(self):
-        return self.vreg.config
-
-    def session(self):
-        """return current server side session (using default manager account)"""
-        return self.env.repo._sessions[self.env.cnx.sessionid]
-
-    def request(self, *args, **kwargs):
-        """return a web interface request"""
-        return self.env.create_request(*args, **kwargs)
-
-    @nocoverage
-    def rset_and_req(self, *args, **kwargs):
-        return self.env.get_rset_and_req(*args, **kwargs)
-
-    def entity(self, rql, args=None, eidkey=None, req=None):
-        return self.execute(rql, args, eidkey, req=req).get_entity(0, 0)
-
-    def etype_instance(self, etype, req=None):
-        req = req or self.request()
-        e = self.env.vreg['etypes'].etype_class(etype)(req)
-        e.eid = None
-        return e
-
-    def add_entity(self, etype, **kwargs):
-        rql = ['INSERT %s X' % etype]
-
-        # dict for replacement in RQL Request
-        rql_args = {}
-
-        if kwargs: #
-            rql.append(':')
-            # dict to define new entities variables
-            entities = {}
-
-            # assignement part of the request
-            sub_rql = []
-            for key, value in kwargs.iteritems():
-                # entities
-                if hasattr(value, 'eid'):
-                    new_value = "%s__" % key.upper()
-
-                    entities[new_value] = value.eid
-                    rql_args[new_value] = value.eid
-
-                    sub_rql.append("X %s %s" % (key, new_value))
-                # final attributes
-                else:
-                    sub_rql.append('X %s %%(%s)s' % (key, key))
-                    rql_args[key] = value
-            rql.append(', '.join(sub_rql))
-
-
-            if entities:
-                rql.append('WHERE')
-                # WHERE part of the request (to link entity to they eid)
-                sub_rql = []
-                for key, value in entities.iteritems():
-                    sub_rql.append("%s eid %%(%s)s" % (key, key))
-                rql.append(', '.join(sub_rql))
-
-        rql = ' '.join(rql)
-        rset = self.execute(rql, rql_args)
-        return rset.get_entity(0, 0)
-
-    def set_option(self, optname, value):
-        self.vreg.config.global_set_option(optname, value)
-
-    def pviews(self, req, rset):
-        return sorted((a.id, a.__class__) for a in self.vreg['views'].possible_views(req, rset=rset))
-
-    def pactions(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
-        return [(a.id, a.__class__) for a in self.vreg['actions'].possible_vobjects(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['actions'].possible_vobjects(req, rset=rset)
-                if a.category in categories]
-
-    paddrelactions = deprecated()(pactions_by_cats)
-
-    def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
-        res = {}
-        for a in self.vreg['actions'].possible_vobjects(req, rset=rset):
-            if a.category not in skipcategories:
-                res.setdefault(a.category, []).append(a.__class__)
-        return res
-
-
-    def remote_call(self, fname, *args):
-        """remote call simulation"""
-        dump = simplejson.dumps
-        args = [dump(arg) for arg in args]
-        req = self.request(fname=fname, pageid='123', arg=args)
-        ctrl = self.vreg['controllers'].select('json', req)
-        return ctrl.publish(), req
-
-    # default test setup and teardown #########################################
-
-    def setup_database(self):
-        pass
-
-    def setUp(self):
-        self.restore_connection()
-        session = self.session()
-        #self.maxeid = self.execute('Any MAX(X)')
-        session.set_pool()
-        self.maxeid = session.system_sql('SELECT MAX(eid) FROM entities').fetchone()[0]
-        self.app = self.env.app
-        self.vreg = self.env.app.vreg
-        self.schema = self.vreg.schema
-        self.vreg.config.mode = 'test'
-        # set default-dest-addrs to a dumb email address to avoid mailbox or
-        # mail queue pollution
-        self.set_option('default-dest-addrs', ['whatever'])
-        self.setup_database()
-        self.commit()
-        MAILBOX[:] = [] # reset mailbox
-
-    @nocoverage
-    def tearDown(self):
-        self.rollback()
-        # self.env.restore_database()
-        self.env.restore_connection()
-        self.session().unsafe_execute('DELETE Any X WHERE X eid > %s' % self.maxeid)
-        self.commit()
-
-    # global resources accessors ###############################################
-
-# XXX
-try:
-    from cubicweb.web import Redirect
-    from urllib import unquote
-except ImportError:
-    pass # cubicweb-web not installed
-else:
-    class ControllerTC(EnvBasedTC):
-        def setUp(self):
-            super(ControllerTC, self).setUp()
-            self.req = self.request()
-            self.ctrl = self.vreg['controllers'].select('edit', self.req)
-
-        def publish(self, req):
-            assert req is self.ctrl.req
-            try:
-                result = self.ctrl.publish()
-                req.cnx.commit()
-            except Redirect:
-                req.cnx.commit()
-                raise
-            return result
-
-        def expect_redirect_publish(self, req=None):
-            if req is not None:
-                self.ctrl = self.vreg['controllers'].select('edit', req)
-            else:
-                req = self.req
-            try:
-                res = self.publish(req)
-            except Redirect, ex:
-                try:
-                    path, params = ex.location.split('?', 1)
-                except:
-                    path, params = ex.location, ""
-                req._url = path
-                cleanup = lambda p: (p[0], unquote(p[1]))
-                params = dict(cleanup(p.split('=', 1)) for p in params.split('&') if p)
-                return req.relative_path(False), params # path.rsplit('/', 1)[-1], params
-            else:
-                self.fail('expected a Redirect exception')
-
-
-def make_late_binding_repo_property(attrname):
-    @property
-    def late_binding(self):
-        """builds cnx as late as possible"""
-        if not hasattr(self, attrname):
-            # sets explicit test mode here to avoid autoreload
-            from cubicweb.cwconfig import CubicWebConfiguration
-            CubicWebConfiguration.mode = 'test'
-            cls = self.__class__
-            config = self.repo_config or TestServerConfiguration('data')
-            cls._repo, cls._cnx = init_test_database('sqlite',  config=config)
-        return getattr(self, attrname)
-    return late_binding
-
-
-class autorepo(type):
-    """automatically set repository on RepositoryBasedTC subclasses if necessary
-    """
-    def __new__(mcs, name, bases, classdict):
-        repo = classdict.get('repo')
-        # try to find repo in one of the base classes
-        if repo is None:
-            for base in bases:
-                repo = getattr(base, 'repo', None)
-                if repo is not None:
-                    classdict['repo'] = repo
-                    break
-        if name != 'RepositoryBasedTC' and not classdict.get('repo'):
-            classdict['repo'] = make_late_binding_repo_property('_repo')
-            classdict['cnx'] = make_late_binding_repo_property('_cnx')
-        return super(autorepo, mcs).__new__(mcs, name, bases, classdict)
-
-
-class RepositoryBasedTC(TestCase):
-    """abstract class for test using direct repository connections
-    """
-    __metaclass__ = autorepo
-    repo_config = None # set a particular config instance if necessary
-
-    # user / session management ###############################################
-
-    def create_user(self, user, groups=('users',), password=None, commit=True):
-        if password is None:
-            password = user
-        eid = self.execute('INSERT CWUser X: X login %(x)s, X upassword %(p)s,'
-                            'X in_state S WHERE S name "activated"',
-                            {'x': unicode(user), 'p': password})[0][0]
-        groups = ','.join(repr(group) for group in groups)
-        self.execute('SET X in_group Y WHERE X eid %%(x)s, Y name IN (%s)' % groups,
-                      {'x': eid})
-        if commit:
-            self.commit()
-        self.session.reset_pool()
-        return eid
-
-    def login(self, login, password=None):
-        cnx = repo_connect(self.repo, unicode(login), password or login,
-                           ConnectionProperties('inmemory'))
-        self.cnxs.append(cnx)
-        return cnx
-
-    def current_session(self):
-        return self.repo._sessions[self.cnxs[-1].sessionid]
-
-    def restore_connection(self):
-        assert len(self.cnxs) == 1, self.cnxs
-        cnx = self.cnxs.pop()
-        try:
-            cnx.close()
-        except Exception, ex:
-            print "exception occured while closing connection", ex
-
-    # db api ##################################################################
-
-    def execute(self, rql, args=None, eid_key=None):
-        assert self.session.id == self.cnxid
-        rset = self.__execute(self.cnxid, rql, args, eid_key)
-        rset.vreg = self.vreg
-        rset.req = self.session
-        # call to set_pool is necessary to avoid pb when using
-        # instance entities for convenience
-        self.session.set_pool()
-        return rset
-
-    def commit(self):
-        self.__commit(self.cnxid)
-        self.session.set_pool()
-
-    def rollback(self):
-        self.__rollback(self.cnxid)
-        self.session.set_pool()
-
-    def close(self):
-        self.__close(self.cnxid)
-
-    # other utilities #########################################################
-
-    def set_debug(self, debugmode):
-        from cubicweb.server import set_debug
-        set_debug(debugmode)
-
-    def set_option(self, optname, value):
-        self.vreg.config.global_set_option(optname, value)
-
-    def add_entity(self, etype, **kwargs):
-        restrictions = ', '.join('X %s %%(%s)s' % (key, key) for key in kwargs)
-        rql = 'INSERT %s X' % etype
-        if kwargs:
-            rql += ': %s' % ', '.join('X %s %%(%s)s' % (key, key) for key in kwargs)
-        rset = self.execute(rql, kwargs)
-        return rset.get_entity(0, 0)
-
-    def default_user_password(self):
-        config = self.repo.config #TestConfiguration('data')
-        user = unicode(config.sources()['system']['db-user'])
-        passwd = config.sources()['system']['db-password']
-        return user, passwd
-
-    def close_connections(self):
-        for cnx in self.cnxs:
-            try:
-                cnx.rollback()
-                cnx.close()
-            except:
-                continue
-        self.cnxs = []
-
-    pactions = EnvBasedTC.pactions.im_func
-    pactionsdict = EnvBasedTC.pactionsdict.im_func
-
-    # default test setup and teardown #########################################
-
-    def _prepare(self):
-        MAILBOX[:] = [] # reset mailbox
-        if hasattr(self, 'cnxid'):
-            return
-        repo = self.repo
-        self.__execute = repo.execute
-        self.__commit = repo.commit
-        self.__rollback = repo.rollback
-        self.__close = repo.close
-        self.cnxid = self.cnx.sessionid
-        self.session = repo._sessions[self.cnxid]
-        self.cnxs = []
-        # reset caches, they may introduce bugs among tests
-        repo._type_source_cache = {}
-        repo._extid_cache = {}
-        repo.querier._rql_cache = {}
-        for source in repo.sources:
-            source.reset_caches()
-        for s in repo.sources:
-            if hasattr(s, '_cache'):
-                s._cache = {}
-
-    @property
-    def config(self):
-        return self.repo.config
-
-    @property
-    def vreg(self):
-        return self.repo.vreg
-
-    @property
-    def schema(self):
-        return self.repo.schema
-
-    def setUp(self):
-        self._prepare()
-        self.session.set_pool()
-        self.maxeid = self.session.system_sql('SELECT MAX(eid) FROM entities').fetchone()[0]
-
-    def tearDown(self):
-        self.close_connections()
-        self.rollback()
-        self.session.unsafe_execute('DELETE Any X WHERE X eid > %(x)s', {'x': self.maxeid})
-        self.commit()
-
--- a/devtools/devctl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/devctl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -20,12 +20,13 @@
 from logilab.common.shellutils import ASK
 from logilab.common.clcommands import register_commands
 
-from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage, underline_title
+from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage
 from cubicweb.__pkginfo__ import version as cubicwebversion
-from cubicweb.toolsutils import Command, copy_skeleton
+from cubicweb.toolsutils import Command, copy_skeleton, underline_title
 from cubicweb.web.webconfig import WebConfiguration
 from cubicweb.server.serverconfig import ServerConfiguration
 
+
 class DevCubeConfiguration(ServerConfiguration, WebConfiguration):
     """dummy config to get full library schema and entities"""
     creating = True
@@ -119,6 +120,7 @@
         libschema = libconfig.load_schema(remove_unused_rtypes=False)
         entities = [e for e in schema.entities() if not e in libschema]
     else:
+        libschema = None
         entities = schema.entities()
     done = set()
     for eschema in sorted(entities):
@@ -203,7 +205,7 @@
                 objid = '%s_%s' % (reg, obj.id)
                 if objid in done:
                     break
-                if obj.property_defs:
+                if obj.cw_property_defs:
                     yield objid
                     done.add(objid)
                     break
--- a/devtools/fake.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/fake.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,12 +7,11 @@
 """
 __docformat__ = "restructuredtext en"
 
-from logilab.common.testlib import mock_object as Mock
 from logilab.common.adbh import get_adv_func_helper
 
 from indexer import get_indexer
 
-from cubicweb import RequestSessionMixIn
+from cubicweb.req import RequestSessionBase
 from cubicweb.web.request import CubicWebRequestBase
 from cubicweb.devtools import BASE_URL, BaseApptestConfiguration
 
@@ -174,7 +173,7 @@
         return True
 
 
-class FakeSession(RequestSessionMixIn):
+class FakeSession(RequestSessionBase):
     def __init__(self, repo=None, user=None):
         self.repo = repo
         self.vreg = getattr(self.repo, 'vreg', FakeVReg())
--- a/devtools/htmlparser.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/htmlparser.py	Fri Aug 14 00:02:08 2009 +0200
@@ -169,3 +169,5 @@
                 except KeyError:
                     continue
         return False
+
+VALMAP = {None: None, 'dtd': DTDValidator, 'xml': SaxOnlyValidator}
--- a/devtools/livetest.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/livetest.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,9 +6,10 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
+import os
 import socket
 import logging
-from os.path import join, dirname, exists
+from os.path import join, dirname, normpath, abspath
 from StringIO import StringIO
 
 #from twisted.application import service, strports
@@ -21,10 +22,9 @@
 
 from logilab.common.testlib import TestCase
 
-import cubicweb.web
 from cubicweb.dbapi import in_memory_cnx
 from cubicweb.etwist.server import CubicWebRootResource
-from cubicweb.devtools import LivetestConfiguration, init_test_database
+from cubicweb.devtools import BaseApptestConfiguration, init_test_database
 
 
 
@@ -50,25 +50,57 @@
 
 
 
+class LivetestConfiguration(BaseApptestConfiguration):
+    init_repository = False
+
+    def __init__(self, cube=None, sourcefile=None, pyro_name=None,
+                 log_threshold=logging.CRITICAL):
+        BaseApptestConfiguration.__init__(self, cube, log_threshold=log_threshold)
+        self.appid = pyro_name or cube
+        # don't change this, else some symlink problems may arise in some
+        # environment (e.g. mine (syt) ;o)
+        # XXX I'm afraid this test will prevent to run test from a production
+        # environment
+        self._sources = None
+        # 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:
+            # cube test
+            self.apphome = abspath('..')
+        self.sourcefile = sourcefile
+        self.global_set_option('realm', '')
+        self.use_pyro = pyro_name is not None
+
+    def pyro_enabled(self):
+        if self.use_pyro:
+            return True
+        else:
+            return False
+
+
+
 def make_site(cube, options=None):
     from cubicweb.etwist import twconfig # trigger configuration registration
-    sourcefile = options.sourcefile
-    config = LivetestConfiguration(cube, sourcefile,
+    config = LivetestConfiguration(cube, options.sourcefile,
                                    pyro_name=options.pyro_name,
                                    log_threshold=logging.DEBUG)
-    source = config.sources()['system']
-    init_test_database(driver=source['db-driver'], config=config)
+    init_test_database(config=config)
     # if '-n' in sys.argv: # debug mode
     cubicweb = LivetestResource(config, debug=True)
     toplevel = cubicweb
     website = server.Site(toplevel)
     cube_dir = config.cube_dir(cube)
+    source = config.sources()['system']
     for port in xrange(7777, 7798):
         try:
             reactor.listenTCP(port, channel.HTTPFactory(website))
             saveconf(cube_dir, port, source['db-user'], source['db-password'])
             break
-        except CannotListenError, exc:
+        except CannotListenError:
             print "port %s already in use, I will try another one" % port
     else:
         raise
--- a/devtools/migrtest.py	Fri Aug 14 00:01:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-"""Migration test script
-
-* migration will be played into a chroot of the local machine
-* the database server used can be configured
-* test tested instance may be on another host
-
-
-We are using postgres'.pgpass file. Here is a copy of postgres documentation
-about that:
-
-The file .pgpass in a user's home directory or the file referenced by
-PGPASSFILE can contain passwords to be used if the connection requires
-a password (and no password has been specified otherwise).
-
-
-This file should contain lines of the following format:
-
-hostname:port:database:username:password
-
-Each of the first four fields may be a literal value, or *, which
-matches anything. The password field from the first line that matches
-the current connection parameters will be used. (Therefore, put
-more-specific entries first when you are using wildcards.) If an entry
-needs to contain : or \, escape this character with \. A hostname of
-localhost matches both host (TCP) and local (Unix domain socket)
-connections coming from the local machine.
-
-The permissions on .pgpass must disallow any access to world or group;
-achieve this by the command chmod 0600 ~/.pgpass. If the permissions
-are less strict than this, the file will be ignored.
-
-: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"
-
-from os import system
-from os.path import join, basename
-
-from logilab.common.shellutils import cp, rm
-
-from cubicweb.toolsutils import read_config
-from cubicweb.server.utils import generate_sources_file
-
-# XXXX use db-copy instead
-
-# test environment configuration
-chrootpath = '/sandbox/cubicwebtest'
-tmpdbhost = 'crater'
-tmpdbuser = 'syt'
-tmpdbpasswd = 'syt'
-
-def play_migration(applhome, applhost='', sudo=False):
-    applid = dbname = basename(applhome)
-    testapplhome = join(chrootpath, applhome)
-    # copy instance into the chroot
-    if applhost:
-        system('scp -r %s:%s %s' % (applhost, applhome, testapplhome))
-    else:
-        cp(applhome, testapplhome)
-##     # extract db parameters
-##     sources = read_config(join(testapplhome, 'sources'))
-##     dbname = sources['system']['db-name']
-##     dbhost = sources['system'].get('db-host') or ''
-##     dbuser = sources['system'].get('db-user') or ''
-##     dbpasswd = sources['system'].get('db-password') or ''
-    # generate sources file
-    # XXX multisources
-    sources = {'system': {}}
-    sources['system']['db-encoding'] = 'UTF8' # XXX
-    sources['system']['db-name'] = dbname
-    sources['system']['db-host'] = None
-    sources['system']['db-user'] = tmpdbuser
-    sources['system']['db-password'] = None
-    generate_sources_file(join(testapplhome, 'sources'), sources)
-##     # create postgres password file so we won't need anymore passwords
-##     # XXX may exist!
-##     pgpassfile = expanduser('~/.pgpass')
-##     pgpass = open(pgpassfile, 'w')
-##     if dbpasswd:
-##         pgpass.write('%s:*:%s:%s:%s\n' % (dbhost or applhost or 'localhost',
-##                                           dbname, dbuser, dbpasswd))
-##     if tmpdbpasswd:
-##         pgpass.write('%s:*:%s:%s:%s\n' % (tmpdbhost or 'localhost', dbname,
-##                                           tmpdbuser, tmpdbpasswd))
-##     pgpass.close()
-##     chmod(pgpassfile, 0600)
-    # dump db
-##     dumpcmd = 'pg_dump -Fc -U %s -f /tmp/%s.dump %s' % (
-##         dbuser, dbname, dbname)
-##     if dbhost:
-##         dumpcmd += ' -h %s' % dbhost
-    dumpfile = '/tmp/%s.dump' % applid
-    dumpcmd = 'cubicweb-ctl db-dump --output=%s %s' % (dumpfile, applid)
-    if sudo:
-        dumpcmd = 'sudo %s' % dumpcmd
-    if applhost:
-        dumpcmd = 'ssh %s "%s"' % (applhost, dumpcmd)
-    if system(dumpcmd):
-        raise Exception('error while dumping the database')
-##     if not dbhost and applhost:
-    if applhost:
-        # retrieve the dump
-        if system('scp %s:%s %s' % (applhost, dumpfile, dumpfile)):
-            raise Exception('error while retreiving the dump')
-    # move the dump into the chroot
-    system('mv %s %s%s' % (dumpfile, chrootpath, dumpfile))
-    # locate installed versions
-    vcconf = read_config(join(testapplhome, 'vc.conf'))
-    template = vcconf['TEMPLATE']
-    cubicwebversion = vcconf['CW']
-    templversion = vcconf['TEMPLATE_VERSION']
-    # install the same versions cubicweb and template versions into the chroot
-    system('sudo chroot %s apt-get update' % chrootpath)
-    system('sudo chroot %s apt-get install cubicweb-server=%s cubicweb-client=%s'
-           % (chrootpath, cubicwebversion, cubicwebversion))
-    system('sudo chroot %s apt-get install cubicweb-%s-appl-server=%s cubicweb-%s-appl-client=%s'
-           % (chrootpath, template, templversion, template, templversion))
-    # update and upgrade to the latest version
-    system('sudo chroot %s apt-get install cubicweb-server cubicweb-client' % chrootpath)
-    system('sudo chroot %s apt-get install cubicweb-%s-appl-server cubicweb-%s-appl-client'
-           % (chrootpath, template, template))
-    # create and fill the database
-    system('sudo chroot cubicweb-ctl db-restore %s %s' % (applid, dumpfile))
-##     if not tmpdbhost:
-##         system('createdb -U %s -T template0 -E UTF8 %s' % (tmpdbuser, dbname))
-##         system('pg_restore -U %s -O -Fc -d %s /tmp/%s.dump'
-##                % (tmpdbuser, dbname, dbname))
-##     else:
-##         system('createdb -h %s -U %s -T template0 -E UTF8 %s'
-##                % (tmpdbhost, tmpdbuser, dbname))
-##         system('pg_restore -h %s -U %s -O -Fc -d %s /tmp/%s.dump'
-##                % (tmpdbhost, tmpdbuser, dbname, dbname))
-    # launch upgrade
-    system('sudo chroot %s cubicweb-ctl upgrade %s' % (chrootpath, applid))
-
-    # cleanup
-    rm(testapplhome)
-##     rm(pgpassfile)
-##     if tmpdbhost:
-##         system('dropdb -h %s -U %s %s' % (tmpdbuser, tmpdbhost, dbname))
-##     else:
-##         system('dropdb -U %s %s' % (tmpdbuser, dbname))
-##     if not dbhost and applhost:
-    if applhost:
-        system('ssh %s rm %s' % (applhost, dumpfile))
-    rm('%s%s' % (chrootpath, dumpfile))
-
-
-if __name__ == '__main__':
-    play_migration('/etc/cubicweb.d/jpl', 'lepus')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/realdbtest.py	Fri Aug 14 00:02:08 2009 +0200
@@ -0,0 +1,43 @@
+import logging
+from cubicweb import toolsutils
+from cubicweb.devtools import DEFAULT_SOURCES, BaseApptestConfiguration
+
+class RealDatabaseConfiguration(BaseApptestConfiguration):
+    init_repository = False
+    sourcesdef =  DEFAULT_SOURCES.copy()
+
+    def sources(self):
+        """
+        By default, we run tests with the sqlite DB backend.
+        One may use its own configuration by just creating a
+        'sources' file in the test directory from wich tests are
+        launched.
+        """
+        self._sources = self.sourcesdef
+        return self._sources
+
+
+def buildconfig(dbuser, dbpassword, dbname, adminuser, adminpassword, dbhost=None):
+    """convenience function that builds a real-db configuration class"""
+    sourcesdef =  {'system': {'adapter' : 'native',
+                              'db-encoding' : 'UTF-8', #'ISO-8859-1',
+                              'db-user' : dbuser,
+                              'db-password' : dbpassword,
+                              'db-name' : dbname,
+                              'db-driver' : 'postgres',
+                              'db-host' : dbhost,
+                              },
+                   'admin' : {'login': adminuser,
+                              'password': adminpassword,
+                              },
+                   }
+    return type('MyRealDBConfig', (RealDatabaseConfiguration,),
+                {'sourcesdef': sourcesdef})
+
+
+def loadconfig(filename):
+    """convenience function that builds a real-db configuration class
+    from a file
+    """
+    return type('MyRealDBConfig', (RealDatabaseConfiguration,),
+                {'sourcesdef': toolsutils.read_config(filename)})
--- a/devtools/test/unittest_testlib.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/test/unittest_testlib.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,11 +10,11 @@
 from unittest import TestSuite
 
 
-from logilab.common.testlib import (TestCase, unittest_main, mock_object,
+from logilab.common.testlib import (TestCase, unittest_main, 
                                     SkipAwareTextTestRunner)
+
 from cubicweb.devtools import htmlparser
-
-from cubicweb.devtools.testlib import WebTest, EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 class WebTestTC(TestCase):
 
@@ -23,7 +23,7 @@
         self.runner = SkipAwareTextTestRunner(stream=output)
 
     def test_error_raised(self):
-        class MyWebTest(WebTest):
+        class MyWebTest(CubicWebTC):
 
             def test_error_view(self):
                 self.add_entity('Bug', title=u"bt")
@@ -39,7 +39,7 @@
         self.assertEquals(len(result.failures), 1)
 
 
-class TestLibTC(EnvBasedTC):
+class TestLibTC(CubicWebTC):
     def test_add_entity_with_relation(self):
         bug = self.add_entity(u'Bug', title=u"toto")
         self.add_entity(u'Bug', title=u"tata", identical_to=bug)
--- a/devtools/testlib.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/devtools/testlib.py	Fri Aug 14 00:02:08 2009 +0200
@@ -1,4 +1,4 @@
-"""this module contains base classes for web tests
+"""this module contains base classes and utilities for cubicweb tests
 
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -7,84 +7,505 @@
 """
 __docformat__ = "restructuredtext en"
 
+import os
 import sys
+import re
+from urllib import unquote
 from math import log
 
-from logilab.common.debugger import Debugger
-from logilab.common.testlib import InnerTest
-from logilab.common.pytest import nocoverage
+import simplejson
+
+import yams.schema
 
-from cubicweb.devtools import VIEW_VALIDATORS
-from cubicweb.devtools.apptest import EnvBasedTC
-from cubicweb.devtools._apptest import unprotected_entities, SYSTEM_RELATIONS
-from cubicweb.devtools.htmlparser import DTDValidator, SaxOnlyValidator, HTMLValidator
-from cubicweb.devtools.fill import insert_entity_queries, make_relations_queries
+from logilab.common.testlib import TestCase, InnerTest
+from logilab.common.pytest import nocoverage, pause_tracing, resume_tracing
+from logilab.common.debugger import Debugger
+from logilab.common.umessage import message_from_string
+from logilab.common.decorators import cached, classproperty
+from logilab.common.deprecation import deprecated
 
-from cubicweb.sobjects.notification import NotificationView
-
-from cubicweb.vregistry import NoSelectableObject
+from cubicweb import NoSelectableObject, cwconfig, devtools, web, server
+from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError
+from cubicweb.sobjects import notification
+from cubicweb.web import application
+from cubicweb.devtools import SYSTEM_ENTITIES, SYSTEM_RELATIONS, VIEW_VALIDATORS
+from cubicweb.devtools import fake, htmlparser
 
 
-## TODO ###############
-# creation tests: make sure an entity was actually created
-# Existing Test Environment
+# low-level utilities ##########################################################
 
 class CubicWebDebugger(Debugger):
-
+    """special debugger class providing a 'view' function which saves some
+    html into a temporary file and open a web browser to examinate it.
+    """
     def do_view(self, arg):
         import webbrowser
         data = self._getval(arg)
         file('/tmp/toto.html', 'w').write(data)
         webbrowser.open('file:///tmp/toto.html')
 
-def how_many_dict(schema, cursor, how_many, skip):
-    """compute how many entities by type we need to be able to satisfy relations
-    cardinality
-    """
-    # compute how many entities by type we need to be able to satisfy relation constraint
-    relmap = {}
-    for rschema in schema.relations():
-        if rschema.is_final():
-            continue
-        for subj, obj in rschema.iter_rdefs():
-            card = rschema.rproperty(subj, obj, 'cardinality')
-            if card[0] in '1?' and len(rschema.subjects(obj)) == 1:
-                relmap.setdefault((rschema, subj), []).append(str(obj))
-            if card[1] in '1?' and len(rschema.objects(subj)) == 1:
-                relmap.setdefault((rschema, obj), []).append(str(subj))
-    unprotected = unprotected_entities(schema)
-    for etype in skip:
-        unprotected.add(etype)
-    howmanydict = {}
-    for etype in unprotected_entities(schema, strict=True):
-        howmanydict[str(etype)] = cursor.execute('Any COUNT(X) WHERE X is %s' % etype)[0][0]
-        if etype in unprotected:
-            howmanydict[str(etype)] += how_many
-    for (rschema, etype), targets in relmap.iteritems():
-        # XXX should 1. check no cycle 2. propagate changes
-        relfactor = sum(howmanydict[e] for e in targets)
-        howmanydict[str(etype)] = max(relfactor, howmanydict[etype])
-    return howmanydict
-
 
 def line_context_filter(line_no, center, before=3, after=None):
     """return true if line are in context
-    if after is None: after = before"""
+
+    if after is None: after = before
+    """
     if after is None:
         after = before
     return center - before <= line_no <= center + after
 
-## base webtest class #########################################################
-VALMAP = {None: None, 'dtd': DTDValidator, 'xml': SaxOnlyValidator}
+
+def unprotected_entities(schema, strict=False):
+    """returned a set of each non final entity type, excluding "system" entities
+    (eg CWGroup, CWUser...)
+    """
+    if strict:
+        protected_entities = yams.schema.BASE_TYPES
+    else:
+        protected_entities = yams.schema.BASE_TYPES.union(SYSTEM_ENTITIES)
+    return set(schema.entities()) - protected_entities
+
+
+def get_versions(self, checkversions=False):
+    """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.
+
+    replace Repository.get_versions by this method if you don't want versions
+    checking
+    """
+    vcconf = {'cubicweb': self.config.cubicweb_version()}
+    self.config.bootstrap_cubes()
+    for pk in self.config.cubes():
+        version = self.config.cube_version(pk)
+        vcconf[pk] = version
+    self.config._cubes = None
+    return vcconf
+
+
+def refresh_repo(repo):
+    devtools.reset_test_database(repo.config)
+    for pool in repo.pools:
+        pool.reconnect()
+    repo._type_source_cache = {}
+    repo._extid_cache = {}
+    repo.querier._rql_cache = {}
+    for source in repo.sources:
+        source.reset_caches()
+
+
+# email handling, to test emails sent by an application ########################
+
+MAILBOX = []
+
+class Email:
+    """you'll get instances of Email into MAILBOX during tests that trigger
+    some notification.
+
+    * `msg` is the original message object
+
+    * `recipients` is a list of email address which are the recipients of this
+      message
+    """
+    def __init__(self, recipients, msg):
+        self.recipients = recipients
+        self.msg = msg
+
+    @property
+    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'))
+
+# the trick to get email into MAILBOX instead of actually sent: monkey patch
+# cwconfig.SMTP object
+class MockSMTP:
+    def __init__(self, server, port):
+        pass
+    def close(self):
+        pass
+    def sendmail(self, helo_addr, recipients, msg):
+        MAILBOX.append(Email(recipients, msg))
+
+cwconfig.SMTP = MockSMTP
+
+
+# base class for cubicweb tests requiring a full cw environments ###############
+
+class CubicWebTC(TestCase):
+    """abstract class for test using an apptest environment
+
+    attributes:
+    `vreg`, the vregistry
+    `schema`, self.vreg.schema
+    `config`, cubicweb configuration
+    `cnx`, dbapi connection to the repository using an admin user
+    `session`, server side session associated to `cnx`
+    `app`, the cubicweb publisher (for web testing)
+    `repo`, the repository object
+
+    `admlogin`, login of the admin user
+    `admpassword`, password of the admin user
+
+    """
+    appid = 'data'
+    configcls = devtools.ApptestConfiguration
+
+    @classproperty
+    def config(cls):
+        """return the configuration object. Configuration is cached on the test
+        class.
+        """
+        try:
+            return cls.__dict__['_config']
+        except KeyError:
+            config = cls._config = cls.configcls(cls.appid)
+            config.mode = 'test'
+            return config
+
+    @classmethod
+    def init_config(cls, config):
+        """configuration initialization hooks. You may want to override this."""
+        source = config.sources()['system']
+        cls.admlogin = unicode(source['db-user'])
+        cls.admpassword = source['db-password']
+        # uncomment the line below if you want rql queries to be logged
+        #config.global_set_option('query-log-file', '/tmp/test_rql_log.' + `os.getpid()`)
+        config.global_set_option('log-file', None)
+        # set default-dest-addrs to a dumb email address to avoid mailbox or
+        # mail queue pollution
+        config.global_set_option('default-dest-addrs', ['whatever'])
+        try:
+            send_to =  '%s@logilab.fr' % os.getlogin()
+        except OSError:
+            send_to =  '%s@logilab.fr' % (os.environ.get('USER')
+                                          or os.environ.get('USERNAME')
+                                          or os.environ.get('LOGNAME'))
+        config.global_set_option('sender-addr', send_to)
+        config.global_set_option('default-dest-addrs', send_to)
+        config.global_set_option('sender-name', 'cubicweb-test')
+        config.global_set_option('sender-addr', 'cubicweb-test@logilab.fr')
+        # web resources
+        config.global_set_option('base-url', devtools.BASE_URL)
+        try:
+            config.global_set_option('embed-allowed', re.compile('.*'))
+        except: # not in server only configuration
+            pass
+
+    @classmethod
+    def _init_repo(cls):
+        """init the repository and connection to it.
+
+        Repository and connection are cached on the test class. Once
+        initialized, we simply reset connections and repository caches.
+        """
+        if not 'repo' in cls.__dict__:
+            cls._build_repo()
+        else:
+            cls.cnx.rollback()
+            cls._refresh_repo()
+
+    @classmethod
+    def _build_repo(cls):
+        cls.repo, cls.cnx = devtools.init_test_database(config=cls.config)
+        cls.init_config(cls.config)
+        cls.vreg = cls.repo.vreg
+        cls._orig_cnx = cls.cnx
+        cls.config.repository = lambda x=None: cls.repo
+        # necessary for authentication tests
+        cls.cnx.login = cls.admlogin
+        cls.cnx.password = cls.admpassword
+
+    @classmethod
+    def _refresh_repo(cls):
+        refresh_repo(cls.repo)
+
+    # global resources accessors ###############################################
+
+    @property
+    def schema(self):
+        """return the application schema"""
+        return self.vreg.schema
+
+    @property
+    def session(self):
+        """return current server side session (using default manager account)"""
+        return self.repo._sessions[self.cnx.sessionid]
+
+    @property
+    def adminsession(self):
+        """return current server side session (using default manager account)"""
+        return self.repo._sessions[self._orig_cnx.sessionid]
+
+    def set_option(self, optname, value):
+        self.config.global_set_option(optname, value)
+
+    def set_debug(self, debugmode):
+        server.set_debug(debugmode)
+
+    # default test setup and teardown #########################################
+
+    def setUp(self):
+        pause_tracing()
+        self._init_repo()
+        resume_tracing()
+        self.setup_database()
+        self.commit()
+        MAILBOX[:] = [] # reset mailbox
+
+    def setup_database(self):
+        """add your database setup code by overriding this method"""
+
+    # user / session management ###############################################
+
+    def user(self, req=None):
+        """return the application schema"""
+        if req is None:
+            req = self.request()
+            return self.cnx.user(req)
+        else:
+            return req.user
 
-class WebTest(EnvBasedTC):
-    """base class for web tests"""
-    __abstract__ = True
+    def create_user(self, login, groups=('users',), password=None, req=None,
+                    commit=True):
+        """create and return a new user entity"""
+        if password is None:
+            password = login.encode('utf8')
+        cursor = self._orig_cnx.cursor(req or self.request())
+        rset = cursor.execute('INSERT CWUser X: X login %(login)s, X upassword %(passwd)s,'
+                              'X in_state S WHERE S name "activated"',
+                              {'login': unicode(login), 'passwd': password})
+        user = rset.get_entity(0, 0)
+        cursor.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
+                       % ','.join(repr(g) for g in groups),
+                       {'x': user.eid}, 'x')
+        user.clear_related_cache('in_group', 'subject')
+        if commit:
+            self._orig_cnx.commit()
+        return user
+
+    def login(self, login, password=None):
+        """return a connection for the given login/password"""
+        if login == self.admlogin:
+            self.restore_connection()
+        else:
+            self.cnx = repo_connect(self.repo, unicode(login),
+                                    password or str(login),
+                                    ConnectionProperties('inmemory'))
+        if login == self.vreg.config.anonymous_user()[0]:
+            self.cnx.anonymous_connection = True
+        return self.cnx
+
+    def restore_connection(self):
+        if not self.cnx is self._orig_cnx:
+            try:
+                self.cnx.close()
+            except ProgrammingError:
+                pass # already closed
+        self.cnx = self._orig_cnx
+
+    # db api ##################################################################
+
+    @nocoverage
+    def cursor(self, req=None):
+        return self.cnx.cursor(req or self.request())
+
+    @nocoverage
+    def execute(self, rql, args=None, eidkey=None, req=None):
+        """executes <rql>, builds a resultset, and returns a couple (rset, req)
+        where req is a FakeRequest
+        """
+        req = req or self.request(rql=rql)
+        return self.cnx.cursor(req).execute(unicode(rql), args, eidkey)
+
+    @nocoverage
+    def commit(self):
+        self.cnx.commit()
+
+    @nocoverage
+    def rollback(self):
+        try:
+            self.cnx.rollback()
+        except ProgrammingError:
+            pass
+
+    # # server side db api #######################################################
+
+    def sexecute(self, rql, args=None, eid_key=None):
+        self.session.set_pool()
+        return self.session.execute(rql, args, eid_key)
+
+    # other utilities #########################################################
+
+    def entity(self, rql, args=None, eidkey=None, req=None):
+        return self.execute(rql, args, eidkey, req=req).get_entity(0, 0)
+
+    def add_entity(self, etype, req=None, **kwargs):
+        rql = ['INSERT %s X' % etype]
+        # dict for replacement in RQL Request
+        args = {}
+        if kwargs:
+            rql.append(':')
+            # dict to define new entities variables
+            entities = {}
+            # assignement part of the request
+            sub_rql = []
+            for key, value in kwargs.iteritems():
+                # entities
+                if hasattr(value, 'eid'):
+                    new_value = "%s__" % key.upper()
+                    entities[new_value] = value.eid
+                    args[new_value] = value.eid
+
+                    sub_rql.append("X %s %s" % (key, new_value))
+                # final attributes
+                else:
+                    sub_rql.append('X %s %%(%s)s' % (key, key))
+                    args[key] = value
+            rql.append(', '.join(sub_rql))
+            if entities:
+                rql.append('WHERE')
+                # WHERE part of the request (to link entity to they eid)
+                sub_rql = []
+                for key, value in entities.iteritems():
+                    sub_rql.append("%s eid %%(%s)s" % (key, key))
+                rql.append(', '.join(sub_rql))
+        return self.execute(' '.join(rql), args, req=req).get_entity(0, 0)
+
+    # vregistry inspection utilities ###########################################
+
+    def pviews(self, req, rset):
+        return sorted((a.id, a.__class__)
+                      for a in self.vreg['views'].possible_views(req, rset=rset))
 
-    pdbclass = CubicWebDebugger
-    # this is a hook to be able to define a list of rql queries
-    # that are application dependent and cannot be guessed automatically
-    application_rql = []
+    def pactions(self, req, rset,
+                 skipcategories=('addrelated', 'siteactions', 'useractions')):
+        return [(a.id, a.__class__)
+                for a in self.vreg['actions'].poss_visible_objects(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['actions'].poss_visible_objects(req, rset=rset)
+                if a.category in categories]
+
+    def pactionsdict(self, req, rset,
+                     skipcategories=('addrelated', 'siteactions', 'useractions')):
+        res = {}
+        for a in self.vreg['actions'].poss_visible_objects(req, rset=rset):
+            if a.category not in skipcategories:
+                res.setdefault(a.category, []).append(a.__class__)
+        return res
+
+    def list_views_for(self, rset):
+        """returns the list of views that can be applied on `rset`"""
+        req = rset.req
+        only_once_vids = ('primary', 'secondary', 'text')
+        req.data['ex'] = ValueError("whatever")
+        viewsvreg = self.vreg['views']
+        for vid, views in viewsvreg.items():
+            if vid[0] == '_':
+                continue
+            if rset.rowcount > 1 and vid in only_once_vids:
+                continue
+            views = [view for view in views
+                     if view.category != 'startupview'
+                     and not issubclass(view, NotificationView)]
+            if views:
+                try:
+                    view = viewsvreg._select_best(views, req, rset=rset)
+                    if view.linkable():
+                        yield view
+                    else:
+                        not_selected(self.vreg, view)
+                    # else the view is expected to be used as subview and should
+                    # not be tested directly
+                except NoSelectableObject:
+                    continue
+
+    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['actions'].possible_objects(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['boxes'].possible_objects(req, rset=rset):
+            yield box
+
+    def list_startup_views(self):
+        """returns the list of startup views"""
+        req = self.request()
+        for view in self.vreg['views'].possible_views(req, None):
+            if view.category == 'startupview':
+                yield view.id
+            else:
+                not_selected(self.vreg, view)
+
+    # web ui testing utilities #################################################
+
+    @property
+    @cached
+    def app(self):
+        """return a cubicweb publisher"""
+        return application.CubicWebPublisher(self.config, vreg=self.vreg)
+
+    requestcls = fake.FakeRequest
+    def request(self, *args, **kwargs):
+        """return a web ui request"""
+        req = self.requestcls(self.vreg, form=kwargs)
+        req.set_connection(self.cnx)
+        return req
+
+    def remote_call(self, fname, *args):
+        """remote json call simulation"""
+        dump = simplejson.dumps
+        args = [dump(arg) for arg in args]
+        req = self.request(fname=fname, pageid='123', arg=args)
+        ctrl = self.vreg['controllers'].select('json', req)
+        return ctrl.publish(), req
+
+    def publish(self, req):
+        """call the publish method of the edit controller"""
+        ctrl = self.vreg['controllers'].select('edit', req)
+        try:
+            result = ctrl.publish()
+            req.cnx.commit()
+        except web.Redirect:
+            req.cnx.commit()
+            raise
+        return result
+
+    def expect_redirect_publish(self, req):
+        """call the publish method of the edit controller, expecting to get a
+        Redirect exception."""
+        try:
+            self.publish(req)
+        except web.Redirect, ex:
+            try:
+                path, params = ex.location.split('?', 1)
+            except:
+                path, params = ex.location, ""
+            req._url = path
+            cleanup = lambda p: (p[0], unquote(p[1]))
+            params = dict(cleanup(p.split('=', 1)) for p in params.split('&') if p)
+            return req.relative_path(False), params # path.rsplit('/', 1)[-1], params
+        else:
+            self.fail('expected a Redirect exception')
+
+    # content validation #######################################################
 
     # validators are used to validate (XML, DTD, whatever) view's content
     # validators availables are :
@@ -99,8 +520,8 @@
         # snippets
         #'text/html': DTDValidator,
         #'application/xhtml+xml': DTDValidator,
-        'application/xml': SaxOnlyValidator,
-        'text/xml': SaxOnlyValidator,
+        'application/xml': htmlparser.SaxOnlyValidator,
+        'text/xml': htmlparser.SaxOnlyValidator,
         'text/plain': None,
         'text/comma-separated-values': None,
         'text/x-vcard': None,
@@ -109,68 +530,9 @@
         'image/png': None,
         }
     # maps vid : validator name (override content_type_validators)
-    vid_validators = dict((vid, VALMAP[valkey])
+    vid_validators = dict((vid, htmlparser.VALMAP[valkey])
                           for vid, valkey in VIEW_VALIDATORS.iteritems())
 
-    no_auto_populate = ()
-    ignored_relations = ()
-
-    def custom_populate(self, how_many, cursor):
-        pass
-
-    def post_populate(self, cursor):
-        pass
-
-    @nocoverage
-    def auto_populate(self, how_many):
-        """this method populates the database with `how_many` entities
-        of each possible type. It also inserts random relations between them
-        """
-        cu = self.cursor()
-        self.custom_populate(how_many, cu)
-        vreg = self.vreg
-        howmanydict = how_many_dict(self.schema, cu, how_many, self.no_auto_populate)
-        for etype in unprotected_entities(self.schema):
-            if etype in self.no_auto_populate:
-                continue
-            nb = howmanydict.get(etype, how_many)
-            for rql, args in insert_entity_queries(etype, self.schema, vreg, nb):
-                cu.execute(rql, args)
-        edict = {}
-        for etype in unprotected_entities(self.schema, strict=True):
-            rset = cu.execute('%s X' % etype)
-            edict[str(etype)] = set(row[0] for row in rset.rows)
-        existingrels = {}
-        ignored_relations = SYSTEM_RELATIONS + self.ignored_relations
-        for rschema in self.schema.relations():
-            if rschema.is_final() or rschema in ignored_relations:
-                continue
-            rset = cu.execute('DISTINCT Any X,Y WHERE X %s Y' % rschema)
-            existingrels.setdefault(rschema.type, set()).update((x, y) for x, y in rset)
-        q = make_relations_queries(self.schema, edict, cu, ignored_relations,
-                                   existingrels=existingrels)
-        for rql, args in q:
-            cu.execute(rql, args)
-        self.post_populate(cu)
-        self.commit()
-
-    @nocoverage
-    def _check_html(self, output, view, template='main-template'):
-        """raises an exception if the HTML is invalid"""
-        try:
-            validatorclass = self.vid_validators[view.id]
-        except KeyError:
-            if template is None:
-                default_validator = HTMLValidator
-            else:
-                default_validator = DTDValidator
-            validatorclass = self.content_type_validators.get(view.content_type,
-                                                              default_validator)
-        if validatorclass is None:
-            return None
-        validator = validatorclass()
-        return validator.parse_string(output.strip())
-
 
     def view(self, vid, rset=None, req=None, template='main-template',
              **kwargs):
@@ -244,9 +606,145 @@
             raise AssertionError, msg, tcbk
 
 
+    @nocoverage
+    def _check_html(self, output, view, template='main-template'):
+        """raises an exception if the HTML is invalid"""
+        try:
+            validatorclass = self.vid_validators[view.id]
+        except KeyError:
+            if template is None:
+                default_validator = htmlparser.HTMLValidator
+            else:
+                default_validator = htmlparser.DTDValidator
+            validatorclass = self.content_type_validators.get(view.content_type,
+                                                              default_validator)
+        if validatorclass is None:
+            return None
+        validator = validatorclass()
+        return validator.parse_string(output.strip())
+
+    # deprecated ###############################################################
+
+    @deprecated('[3.4] use self.vreg["etypes"].etype_class(etype)(self.request())')
+    def etype_instance(self, etype, req=None):
+        req = req or self.request()
+        e = self.vreg['etypes'].etype_class(etype)(req)
+        e.eid = None
+        return e
+
+    @nocoverage
+    @deprecated('[3.4] use req = self.request(); rset = req.execute()')
+    def rset_and_req(self, rql, optional_args=None, args=None, eidkey=None):
+        """executes <rql>, builds a resultset, and returns a
+        couple (rset, req) where req is a FakeRequest
+        """
+        return (self.execute(rql, args, eidkey),
+                self.request(rql=rql, **optional_args or {}))
+
+
+# auto-populating test classes and utilities ###################################
+
+from cubicweb.devtools.fill import insert_entity_queries, make_relations_queries
+
+def how_many_dict(schema, cursor, how_many, skip):
+    """compute how many entities by type we need to be able to satisfy relations
+    cardinality
+    """
+    # compute how many entities by type we need to be able to satisfy relation constraint
+    relmap = {}
+    for rschema in schema.relations():
+        if rschema.is_final():
+            continue
+        for subj, obj in rschema.iter_rdefs():
+            card = rschema.rproperty(subj, obj, 'cardinality')
+            if card[0] in '1?' and len(rschema.subjects(obj)) == 1:
+                relmap.setdefault((rschema, subj), []).append(str(obj))
+            if card[1] in '1?' and len(rschema.objects(subj)) == 1:
+                relmap.setdefault((rschema, obj), []).append(str(subj))
+    unprotected = unprotected_entities(schema)
+    for etype in skip:
+        unprotected.add(etype)
+    howmanydict = {}
+    for etype in unprotected_entities(schema, strict=True):
+        howmanydict[str(etype)] = cursor.execute('Any COUNT(X) WHERE X is %s' % etype)[0][0]
+        if etype in unprotected:
+            howmanydict[str(etype)] += how_many
+    for (rschema, etype), targets in relmap.iteritems():
+        # XXX should 1. check no cycle 2. propagate changes
+        relfactor = sum(howmanydict[e] for e in targets)
+        howmanydict[str(etype)] = max(relfactor, howmanydict[etype])
+    return howmanydict
+
+
+class AutoPopulateTest(CubicWebTC):
+    """base class for test with auto-populating of the database"""
+    __abstract__ = True
+
+    pdbclass = CubicWebDebugger
+    # this is a hook to be able to define a list of rql queries
+    # that are application dependent and cannot be guessed automatically
+    application_rql = []
+
+    no_auto_populate = ()
+    ignored_relations = ()
+
     def to_test_etypes(self):
         return unprotected_entities(self.schema, strict=True)
 
+    def custom_populate(self, how_many, cursor):
+        pass
+
+    def post_populate(self, cursor):
+        pass
+
+    @nocoverage
+    def auto_populate(self, how_many):
+        """this method populates the database with `how_many` entities
+        of each possible type. It also inserts random relations between them
+        """
+        cu = self.cursor()
+        self.custom_populate(how_many, cu)
+        vreg = self.vreg
+        howmanydict = how_many_dict(self.schema, cu, how_many, self.no_auto_populate)
+        for etype in unprotected_entities(self.schema):
+            if etype in self.no_auto_populate:
+                continue
+            nb = howmanydict.get(etype, how_many)
+            for rql, args in insert_entity_queries(etype, self.schema, vreg, nb):
+                cu.execute(rql, args)
+        edict = {}
+        for etype in unprotected_entities(self.schema, strict=True):
+            rset = cu.execute('%s X' % etype)
+            edict[str(etype)] = set(row[0] for row in rset.rows)
+        existingrels = {}
+        ignored_relations = SYSTEM_RELATIONS | set(self.ignored_relations)
+        for rschema in self.schema.relations():
+            if rschema.is_final() or rschema in ignored_relations:
+                continue
+            rset = cu.execute('DISTINCT Any X,Y WHERE X %s Y' % rschema)
+            existingrels.setdefault(rschema.type, set()).update((x, y) for x, y in rset)
+        q = make_relations_queries(self.schema, edict, cu, ignored_relations,
+                                   existingrels=existingrels)
+        for rql, args in q:
+            cu.execute(rql, args)
+        self.post_populate(cu)
+        self.commit()
+
+    def iter_individual_rsets(self, etypes=None, limit=None):
+        etypes = etypes or self.to_test_etypes()
+        for etype in etypes:
+            if limit:
+                rql = 'Any X LIMIT %s WHERE X is %s' % (limit, etype)
+            else:
+                rql = 'Any X WHERE X is %s' % etype
+            rset = self.execute(rql)
+            for row in xrange(len(rset)):
+                if limit and row > limit:
+                    break
+                # XXX iirk
+                rset2 = rset.limit(limit=1, offset=row)
+                yield rset2
+
     def iter_automatic_rsets(self, limit=10):
         """generates basic resultsets for each entity type"""
         etypes = self.to_test_etypes()
@@ -267,54 +765,6 @@
         for rql in self.application_rql:
             yield self.execute(rql)
 
-
-    def list_views_for(self, rset):
-        """returns the list of views that can be applied on `rset`"""
-        req = rset.req
-        only_once_vids = ('primary', 'secondary', 'text')
-        req.data['ex'] = ValueError("whatever")
-        viewsvreg = self.vreg['views']
-        for vid, views in viewsvreg.items():
-            if vid[0] == '_':
-                continue
-            if rset.rowcount > 1 and vid in only_once_vids:
-                continue
-            views = [view for view in views
-                     if view.category != 'startupview'
-                     and not issubclass(view, NotificationView)]
-            if views:
-                try:
-                    view = viewsvreg.select_best(views, req, rset=rset)
-                    if view.linkable():
-                        yield view
-                    else:
-                        not_selected(self.vreg, view)
-                    # else the view is expected to be used as subview and should
-                    # not be tested directly
-                except NoSelectableObject:
-                    continue
-
-    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['actions'].possible_objects(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['boxes'].possible_objects(req, rset=rset):
-            yield box
-
-    def list_startup_views(self):
-        """returns the list of startup views"""
-        req = self.request()
-        for view in self.vreg['views'].possible_views(req, None):
-            if view.category == 'startupview':
-                yield view.id
-            else:
-                not_selected(self.vreg, view)
-
     def _test_everything_for(self, rset):
         """this method tries to find everything that can be tested
         for `rset` and yields a callable test (as needed in generative tests)
@@ -342,8 +792,16 @@
         return '%s_%s_%s' % ('_'.join(rset.column_types(0)), objid, objtype)
 
 
-class AutomaticWebTest(WebTest):
+# concrete class for automated application testing  ############################
+
+class AutomaticWebTest(AutoPopulateTest):
     """import this if you wan automatic tests to be ran"""
+    def setUp(self):
+        AutoPopulateTest.setUp(self)
+        # access to self.app for proper initialization of the authentication
+        # machinery (else some views may fail)
+        self.app
+
     ## one each
     def test_one_each_config(self):
         self.auto_populate(1)
@@ -365,17 +823,7 @@
             yield self.view, vid, None, req
 
 
-class RealDBTest(WebTest):
-
-    def iter_individual_rsets(self, etypes=None, limit=None):
-        etypes = etypes or unprotected_entities(self.schema, strict=True)
-        for etype in etypes:
-            rset = self.execute('Any X WHERE X is %s' % etype)
-            for row in xrange(len(rset)):
-                if limit and row > limit:
-                    break
-                rset2 = rset.limit(limit=1, offset=row)
-                yield rset2
+# registry instrumentization ###################################################
 
 def not_selected(vreg, appobject):
     try:
@@ -383,16 +831,17 @@
     except (KeyError, AttributeError):
         pass
 
+
 def vreg_instrumentize(testclass):
+    # XXX broken
     from cubicweb.devtools.apptest import TestEnvironment
-    env = testclass._env = TestEnvironment('data', configcls=testclass.configcls,
-                                           requestcls=testclass.requestcls)
+    env = testclass._env = TestEnvironment('data', configcls=testclass.configcls)
     for reg in env.vreg.values():
         reg._selected = {}
         try:
             orig_select_best = reg.__class__.__orig_select_best
         except:
-            orig_select_best = reg.__class__.select_best
+            orig_select_best = reg.__class__._select_best
         def instr_select_best(self, *args, **kwargs):
             selected = orig_select_best(self, *args, **kwargs)
             try:
@@ -402,9 +851,10 @@
             except AttributeError:
                 pass # occurs on reg used to restore database
             return selected
-        reg.__class__.select_best = instr_select_best
+        reg.__class__._select_best = instr_select_best
         reg.__class__.__orig_select_best = orig_select_best
 
+
 def print_untested_objects(testclass, skipregs=('hooks', 'etypes')):
     for regname, reg in testclass._env.vreg.iteritems():
         if regname in skipregs:
--- a/doc/book/en/development/devweb/views.rst	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/en/development/devweb/views.rst	Fri Aug 14 00:02:08 2009 +0200
@@ -124,7 +124,7 @@
        __select__ =implements('Blog')
 
        def cell_call(self, row, col):
-           entity = self.entity(row, col)
+           entity = self.rset.get_entity(row, col)
            self.w(u'<h1>%s</h1>' % entity.title)
            self.w(u'<p>published on %s in category %s</p>' % \
                   (entity.publish_date.strftime('%Y-%m-%d'), entity.category))
@@ -155,7 +155,7 @@
      __select__ =implements('Blog')
 
      def cell_call(self, row, col):
-         entity = self.entity(row, col)
+         entity = self.rset.get_entity(row, col)
          self.w(u'<h1>%s</h1>' % entity.title)
          self.w(u'<p>%s</p>' % entity.description)
          rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid)
--- a/doc/book/en/development/webstdlib/basetemplates.rst	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/en/development/webstdlib/basetemplates.rst	Fri Aug 14 00:02:08 2009 +0200
@@ -126,8 +126,8 @@
                       title=False, message=False)
 
     def get_searchbox(self, view, context):
-        boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
-                                                 view=view, context=context))
+        boxes = list(self.vreg.poss_visible_objects('boxes', self.req, self.rset,
+                                                    view=view, context=context))
         if boxes:
             for box in boxes:
                 if box.id == 'search_box':
--- a/doc/book/en/intro/tutorial/create-cube.rst	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/en/intro/tutorial/create-cube.rst	Fri Aug 14 00:02:08 2009 +0200
@@ -238,7 +238,7 @@
         return entity.view('reledit', rtype='content_format')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
 
         # display entity attributes with prefixes
         self.w(u'<h1>%s</h1>' % entity.title)
--- a/doc/book/fr/01-introduction.fr.txt	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/fr/01-introduction.fr.txt	Fri Aug 14 00:02:08 2009 +0200
@@ -6,13 +6,13 @@
 ===========================
 
 `CubicWeb` nous permet de développer des instances d'applications web
-basées sur un ou plusieurs `cube`. 
+basées sur un ou plusieurs `cube`.
 
-Ce à quoi nous réferrons en parlant de `cube` est un modèle définissant 
-des types de données et des vues. Un `cube` est un composant re-utilisable 
+Ce à quoi nous réferrons en parlant de `cube` est un modèle définissant
+des types de données et des vues. Un `cube` est un composant re-utilisable
 regroupé avec d'autres cubes sur le système de fichiers.
 
-Un `instance` réferre à une installation spécifique d'un ou plusieurs cubes 
+Un `instance` réferre à une installation spécifique d'un ou plusieurs cubes
 où sont regroupés tous les fichiers de configuration de l'application web finale.
 
 Dans ce document, nous allons vous montrer comment créer un cube et l'utiliser
@@ -32,7 +32,7 @@
   |
   |-- data/
   |   |-- cubes.blog.css
-  |   |-- cubes.blog.js  
+  |   |-- cubes.blog.js
   |   |-- external_resources
   |
   |-- debian/
@@ -77,7 +77,7 @@
   |-- views.py
 
 Toute modification apportée à votre modele de données devra
-etre effectué dans ce répertoire. 
+etre effectué dans ce répertoire.
 
 
 
@@ -102,17 +102,17 @@
     title = String(required=True, fulltextindexed=True, maxsize=256)
     publish_date = Date(default='TODAY')
     content = String(required=True, fulltextindexed=True)
-    entry_of = SubjectRelation('Blog', cardinality='?*') 
+    entry_of = SubjectRelation('Blog', cardinality='?*')
 
-Un ``Blog`` a un titre et une description. Le titre est une chaîne 
+Un ``Blog`` a un titre et une description. Le titre est une chaîne
 de caractères requise par la classe parente EntityType et ne doit
-pas excéder 50 caractères. La description est une chaîne de 
+pas excéder 50 caractères. La description est une chaîne de
 caractères sans contraintes.
 
 Une ``BlogEntry`` a un titre, une date de publication et du texte
-étant son contenu. Le titre est une chaîne de caractères qui ne 
+étant son contenu. Le titre est une chaîne de caractères qui ne
 doit pas excéder 100 caractères. La date de publication est de type Date et a
-pour valeur par défaut TODAY, ce qui signifie que lorsqu'une 
+pour valeur par défaut TODAY, ce qui signifie que lorsqu'une
 ``BlogEntry`` sera créée, sa date de publication sera la date
 courante a moins de modifier ce champ. Le texte est une chaîne de
 caractères qui sera indexée en plein texte et sans contraintes.
@@ -121,7 +121,7 @@
 relie à un ``Blog``. La cardinalité ``?*`` signifie que BlogEntry
 peut faire partie de zero a un Blog (``?`` signifie `zero ou un`) et
 qu'un Blog peut avoir une infinité de BlogEntry (``*`` signifie
-`n'importe quel nombre incluant zero`). 
+`n'importe quel nombre incluant zero`).
 Par soucis de complétude, nous rappellerons que ``+`` signifie
 `un ou plus`.
 
@@ -130,7 +130,7 @@
 --------------------
 
 ::
-  
+
   cubicweb-ctl create blog blogdemo
 
 Cette commande va créer un répertoire ``~/etc/cubicweb.d/blogdemo``
@@ -150,7 +150,7 @@
 Vous pouvez à présent accéder  à votre application web vous permettant de
 créer des blogs et d'y poster des messages en visitant l'URL http://localhost:8080/.
 Un premier formulaire d'authentification va vous être proposé. Par défaut,
-l'application n'autorisera pas d'utilisateur anonyme à accéder a votre 
+l'application n'autorisera pas d'utilisateur anonyme à accéder a votre
 application. Vous devrez donc utiliser l'utilisateur administrateur que
 vous aurez crée lors de l'initialisation de votre base de données via
 ``cubicweb-ctl create``.
@@ -166,7 +166,7 @@
 
 Rappelez-vous que pour le moment, tout a été géré par la plate-forme
 `CubicWeb` et que la seule chose qui a été fournie est le schéma de
-données. 
+données.
 
 Créons des entités
 ------------------
@@ -186,7 +186,7 @@
    :alt: from to create blog
 
 En cliquant sur le logo situé dans le coin gauche de la fenêtre,
-vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez 
+vous allez être redirigé vers la page d'accueil. Ensuite, si vous allez
 sur le lien Blog, vous devriez voir la liste des entités Blog, en particulier
 celui que vous venez juste de créer ``Tech-Blog``.
 
@@ -212,7 +212,7 @@
 un peut de texte avant de ``Valider``. Vous venez d'ajouter un article
 sans avoir précisé à quel Blog il appartenait. Dans la colonne de gauche
 se trouve une boite intitulé ``actions``, cliquez sur le menu ``modifier``.
-Vous êtes de retour sur le formulaire d'édition de l'article que vous 
+Vous êtes de retour sur le formulaire d'édition de l'article que vous
 venez de créer, à ceci près que ce formulaire a maintenant une nouvelle
 section intitulée ``ajouter relation``. Choisissez ``entry_of`` dans ce menu,
 cela va faire apparaitre une deuxième menu déroulant dans lequel vous
@@ -225,7 +225,7 @@
    :alt: editing a blog entry to add a relation to a blog
 
 Validez vos modifications en cliquant sur ``Valider``. L'entité article
-qui est listée contient maintenant un lien vers le Blog auquel il 
+qui est listée contient maintenant un lien vers le Blog auquel il
 appartient, ``MyLife``.
 
 .. image:: images/cbw-detail-one-blogentry.fr.png
@@ -248,7 +248,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Une vue est une classe Python qui inclut:
-  
+
   - un identifiant (tous les objets dans `CubicWeb` sont listés
     dans un registre et cet identifiant est utilisé comme la clé)
 
@@ -262,32 +262,32 @@
 des entités que nous cherchons à appliquer. `CubicWeb` utilise un
 sélecteur qui permet de calculer un score et d'identifier la vue
 la plus adaptée au `result set` que nous voulons afficher. La librarie
-standard des sélecteurs se trouve dans ``cubicweb.common.selector`` 
-et une librairie des méthodes utilisées pour calculer les scores 
+standard des sélecteurs se trouve dans ``cubicweb.common.selector``
+et une librairie des méthodes utilisées pour calculer les scores
 est dans ``cubicweb.vregistry.vreq``.
 
 
 Il est possible de définir plusieurs vues ayant le meme identifiant
-et d'y associer des sélecteurs et des filtres afin de permettre à 
-l'application de s'adapter au mieux aux données que nous avons 
+et d'y associer des sélecteurs et des filtres afin de permettre à
+l'application de s'adapter au mieux aux données que nous avons
 besoin d'afficher. Nous verrons cela plus en détails dans :ref:`DefinitionVues`.
 
 On peut citer l'exemple de la vue nommée ``primary`` qui est celle utilisée
-pour visualiser une entité seule. Nous allons vous montrer comment modifier 
+pour visualiser une entité seule. Nous allons vous montrer comment modifier
 cette vue.
 
 Modification des vues
 ~~~~~~~~~~~~~~~~~~~~~
 Si vous souhaitez modifier la manière dont est rendue un article (`Blogentry`),
-vous devez surcharger la vue ``primary`` définie dans le module ``views`` de 
+vous devez surcharger la vue ``primary`` définie dans le module ``views`` de
 votre cube, ``cubes/blog/views.py``.
 
 Nous pourrions par exemple ajouter devant la date de publication un préfixe
 indiquant que la date visualisée est la date de publication.
 
-Pour cela appliquez les modifications suivantes: 
+Pour cela appliquez les modifications suivantes:
 
-:: 
+::
 
   from cubicweb.web.views import baseviews
 
@@ -303,13 +303,13 @@
         return entity.view('reledit', rtype='content_format')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
 
         # display entity attributes with prefixes
         self.w(u'<h1>%s</h1>' % entity.title)
         self.w(u'<p>published on %s</p>' % entity.publish_date.strftime('%Y-%m-%d'))
         self.w(u'<p>%s</p>' % entity.content)
-        
+
         # display relations
         siderelations = []
         if self.main_related_section:
@@ -329,10 +329,10 @@
 
 
 
-Le code que nous avons modifié définit une vue primaire pour une entité de 
-type `BlogEntry`. 
+Le code que nous avons modifié définit une vue primaire pour une entité de
+type `BlogEntry`.
 
-Etant donné que les vues sont appliquées sur des `result sets` et que 
+Etant donné que les vues sont appliquées sur des `result sets` et que
 les `result sets` peuvent être des tableaux de données, il est indispensable
 de récupérer l'entité selon ses coordonnées (row,col).
 
--- a/doc/book/fr/05-define-views.fr.txt	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/fr/05-define-views.fr.txt	Fri Aug 14 00:02:08 2009 +0200
@@ -20,17 +20,17 @@
 * `cell_call(row, col, **kwargs)`, appelle la vue pour une cellule donnée d'un
   result set
 * `url()`, retourne l'url permettant d'obtenir cette vue avec le result set en
-  cours 
+  cours
 * `view(__vid, rset, __fallback_vid=None, **kwargs)`, appelle la vue
   d'identificant `__vid` sur le result set donné. Il est possible de données un
   identificant de vue de "fallback" qui sera utilisé si la vue demandée n'est
   pas applicable au result set
-  
+
 * `wview(__vid, rset, __fallback_vid=None, **kwargs)`, pareil que `view` mais
   passe automatiquement le flux en argument
-  
+
 * `html_headers()`, retourne une liste d'en-tête HTML à placer par le template
-  principal 
+  principal
 
 * `page_title()`, retourne le titre à utiliser dans l'en tête HTML `title`
 
@@ -55,7 +55,7 @@
 
 [FROM-LAX-BOOK]
 
-Tip: when modifying views, you do not need to restart the local 
+Tip: when modifying views, you do not need to restart the local
 server. Just save the file in your editor and reload the page in your
 browser to see the changes.
 
@@ -63,7 +63,7 @@
 
 - an identifier (all objects in `LAX` are entered in a registry
   and this identifier will be used as a key)
-  
+
 - a filter to select the resulsets it can be applied to
 
 `LAX` provides a lot of standard views, for a complete list, you
@@ -83,14 +83,14 @@
   05.     accepts = ('BlogEntry',)
   06.
   07.     def cell_call(self, row, col):
-  08.         entity = self.entity(row, col)
+  08.         entity = self.rset.get_entity(row, col)
   09.         self.w(u'<h1>%s</h1>' % entity.title)
   10.         self.w(u'<p>published on %s in category %s</p>' % \
   11.                (entity.publish_date.strftime('%Y-%m-%d'), entity.category))
   12.         self.w(u'<p>%s</p>' % entity.text)
 
 The above source code defines a new primary view (`line 03`) for
-``BlogEntry`` (`line 05`). 
+``BlogEntry`` (`line 05`).
 
 Since views are applied to resultsets and resulsets can be tables of
 data, it is needed to recover the entity from its (row,col)
@@ -108,11 +108,11 @@
 Let us now improve the primary view of a blog ::
 
   01. class BlogPrimaryView(baseviews.PrimaryView):
-  02. 
+  02.
   03.     accepts = ('Blog',)
   04.
   05.     def cell_call(self, row, col):
-  06.         entity = self.entity(row, col)
+  06.         entity = self.rset.get_entity(row, col)
   07.         self.w(u'<h1>%s</h1>' % entity.title)
   08.         self.w(u'<p>%s</p>' % entity.description)
   09.         rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid)
@@ -127,9 +127,9 @@
 about the schema and infer that such entities have to be of the
 ``BlogEntry`` kind and retrieves them.
 
-The request returns a selection of data called a resultset. At 
+The request returns a selection of data called a resultset. At
 `line 10` the view 'primary' is applied to this resultset to output
-HTML. 
+HTML.
 
 **This is to be compared to interfaces and protocols in object-oriented
 languages. Applying a given view to all the entities of a resultset only
@@ -159,7 +159,7 @@
 
 * create view "blogentry table" with title, publish_date, category
 
-We will show that by default the view that displays 
+We will show that by default the view that displays
 "Any E,D,C WHERE E publish_date D, E category C" is the table view.
 Of course, the same can be obtained by calling
 self.wview('table',rset)
@@ -244,4 +244,4 @@
 ----------------------------------
 Certains navigateurs (dont firefox) n'aime pas les `<div>` vides (par vide
 j'entend sans contenu dans la balise, il peut y avoir des attributs), faut
-toujours mettre `<div></div>` même s'il n'y a rien dedans, et non `<div/>`. 
+toujours mettre `<div></div>` même s'il n'y a rien dedans, et non `<div/>`.
--- a/doc/book/fr/20-01-intro.fr.txt	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/fr/20-01-intro.fr.txt	Fri Aug 14 00:02:08 2009 +0200
@@ -37,7 +37,7 @@
 données manipulées. La syntaxe de la définition est la même que celle
 proposée par `Google AppEngine` mais il faut remplacer la ligne
 d'import::
-  
+
   from google.appengine.ext import db
 
 par celle-ci::
@@ -48,7 +48,7 @@
 Un exemple de schéma de données pour un ``Blog`` pourrait être::
 
   from ginco.goa import db
-  
+
   class BlogEntry(db.Model):
       # un titre à donner à l'entrée
       title = db.StringProperty(required=True)
@@ -57,8 +57,8 @@
       # le contenu de l'entrée
       content = db.TextProperty()
       # une entrée peut en citer une autre
-      cites = db.SelfReferenceProperty() 
-      
+      cites = db.SelfReferenceProperty()
+
 
 Personnalisation des vues
 -------------------------
@@ -75,7 +75,7 @@
 - un identifiant (tous les objets dans `LAX` sont enregistrés
   dans un registre et cet identifiant sert de clé pour y retrouver
   la vue)
-  
+
 - une description des types de données auxquels elle s'applique
 
 Il existe dans `LAX` des vues prédéfinies et utilisées par le moteur
@@ -87,17 +87,17 @@
 Par exemple, si on souhaite modifier la page principale d'une entrée de
 blog, il faut surcharger la vue ``primary`` des objets ``BlogEntry`` dans
 le fichier ``myapp/views.py``::
-  
+
   from ginco.web.views import baseviews
-  
+
   class BlogEntryPrimaryView(baseviews.PrimaryView):
       accepts = ('BlogEntry',)
-      
+
       def cell_call(self, row, col):
-          entity = self.entity(row, col)
+          entity = self.rset.get_entity(row, col)
           self.w(u'<h1>%s</h1>' % entity.title)
           self.w(u'<div>%s</div>' entity.content)
-    
+
 
 Génération du graphique de schéma
 ---------------------------------
@@ -105,13 +105,13 @@
 Il existe une vue ``schema`` qui permet d'afficher un graphique
 représantant les différents types d'entités définis dans le schéma
 ainsi que les relations entre ces types. Ce graphique doit être généré
-statiquement. Le script à utiliser pour générer ce schéma est 
+statiquement. Le script à utiliser pour générer ce schéma est
 dans ``myapp/tools``. Ce script nécessite d'avoir accès aux
 bibliothèques fournies par le SDK de ``Google AppEngine``. Il faut
 donc modifier son PYTHONPATH::
 
   $ export PYTHONPATH=GAE_ROOT/google:GAE_ROOT/lib/yaml
-  $ python tools/generate_schema_img.py 
+  $ python tools/generate_schema_img.py
 
 
 Génération des fichiers de traduction
--- a/doc/book/fr/20-04-develop-views.fr.txt	Fri Aug 14 00:01:12 2009 +0200
+++ b/doc/book/fr/20-04-develop-views.fr.txt	Fri Aug 14 00:02:08 2009 +0200
@@ -13,7 +13,7 @@
 
 - an identifier (all objects in `LAX` are entered in a registry
   and this identifier will be used as a key)
-  
+
 - a filter to select the resulsets it can be applied to
 
 `LAX` provides a lot of standard views, for a complete list, you
@@ -25,13 +25,13 @@
 override the view ``primary`` in ``BlogDemo/views.py`` ::
 
   from ginco.web.views import baseviews
-  
+
   class BlogEntryPrimaryView(baseviews.PrimaryView):
 
       accepts = ('BlogEntry',)
-      
+
       def cell_call(self, row, col):
-          entity = self.entity(row, col)
+          entity = self.rset.get_entity(row, col)
           self.w(u'<h1>%s</h1>' % entity.title)
           self.w(u'<div>%s</div>' % entity.publish_date)
           self.w(u'<div>%s</div>' % entity.category)
@@ -91,7 +91,7 @@
 
 [WRITE ME]
 
-* show how urls are mapped to selections and views and explain URLRewriting 
+* show how urls are mapped to selections and views and explain URLRewriting
 
 Security
 =========
--- a/entities/__init__.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/entities/__init__.py	Fri Aug 14 00:02:08 2009 +0200
@@ -204,85 +204,6 @@
         """
         return ()
 
-    # XXX deprecates, may be killed once old widgets system is gone ###########
-
-    @classmethod
-    def get_widget(cls, rschema, x='subject'):
-        """return a widget to view or edit a relation
-
-        notice that when the relation support multiple target types, the widget
-        is necessarily the same for all those types
-        """
-        # let ImportError propage if web par isn't available
-        from cubicweb.web.widgets import widget
-        if isinstance(rschema, basestring):
-            rschema = cls.schema.rschema(rschema)
-        if x == 'subject':
-            tschema = rschema.objects(cls.e_schema)[0]
-            wdg = widget(cls.vreg, cls, rschema, tschema, 'subject')
-        else:
-            tschema = rschema.subjects(cls.e_schema)[0]
-            wdg = widget(cls.vreg, tschema, rschema, cls, 'object')
-        return wdg
-
-    @deprecated('use EntityFieldsForm.subject_relation_vocabulary')
-    def subject_relation_vocabulary(self, rtype, limit):
-        form = self.vreg.select('forms', 'edition', self.req, entity=self)
-        return form.subject_relation_vocabulary(rtype, limit)
-
-    @deprecated('use EntityFieldsForm.object_relation_vocabulary')
-    def object_relation_vocabulary(self, rtype, limit):
-        form = self.vreg.select('forms', 'edition', self.req, entity=self)
-        return form.object_relation_vocabulary(rtype, limit)
-
-    @deprecated('use AutomaticEntityForm.[e]relations_by_category')
-    def relations_by_category(self, categories=None, permission=None):
-        from cubicweb.web.views.autoform import AutomaticEntityForm
-        return AutomaticEntityForm.erelations_by_category(self, categories, permission)
-
-    @deprecated('use AutomaticEntityForm.[e]srelations_by_category')
-    def srelations_by_category(self, categories=None, permission=None):
-        from cubicweb.web.views.autoform import AutomaticEntityForm
-        return AutomaticEntityForm.esrelations_by_category(self, categories, permission)
-
-    def attribute_values(self, attrname):
-        if self.has_eid() or attrname in self:
-            try:
-                values = self[attrname]
-            except KeyError:
-                values = getattr(self, attrname)
-            # actual relation return a list of entities
-            if isinstance(values, list):
-                return [v.eid for v in values]
-            return (values,)
-        # the entity is being created, try to find default value for
-        # this attribute
-        try:
-            values = self.req.form[attrname]
-        except KeyError:
-            try:
-                values = self[attrname] # copying
-            except KeyError:
-                values = getattr(self, 'default_%s' % attrname,
-                                 self.e_schema.default(attrname))
-                if callable(values):
-                    values = values()
-        if values is None:
-            values = ()
-        elif not isinstance(values, (list, tuple)):
-            values = (values,)
-        return values
-
-    def use_fckeditor(self, attr):
-        """return True if fckeditor should be used to edit entity's attribute named
-        `attr`, according to user preferences
-        """
-        if self.req.use_fckeditor() and self.e_schema.has_metadata(attr, 'format'):
-            if self.has_eid() or '%s_format' % attr in self:
-                return self.attr_metadata(attr, 'format') == 'text/html'
-            return self.req.property_value('ui.default-text-format') == 'text/html'
-        return False
-
 # XXX:  store a reference to the AnyEntity class since it is hijacked in goa
 #       configuration and we need the actual reference to avoid infinite loops
 #       in mro
--- a/entities/lib.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/entities/lib.py	Fri Aug 14 00:02:08 2009 +0200
@@ -84,9 +84,24 @@
         return super(EmailAddress, self).after_deletion_path()
 
 
-from logilab.common.deprecation import class_renamed
-Emailaddress = class_renamed('Emailaddress', EmailAddress)
-Emailaddress.id = 'Emailaddress'
+class Bookmark(AnyEntity):
+    """customized class for Bookmark entities"""
+    id = 'Bookmark'
+    fetch_attrs, fetch_order = fetch_config(['title', 'path'])
+
+    def actual_url(self):
+        url = self.req.build_url(self.path)
+        if self.title:
+            urlparts = list(urlsplit(url))
+            if urlparts[3]:
+                urlparts[3] += '&vtitle=%s' % self.req.url_quote(self.title)
+            else:
+                urlparts[3] = 'vtitle=%s' % self.req.url_quote(self.title)
+            url = urlunsplit(urlparts)
+        return url
+
+    def action_url(self):
+        return self.absolute_url() + '/follow'
 
 
 class CWProperty(AnyEntity):
@@ -111,26 +126,6 @@
         return 'view', {}
 
 
-class Bookmark(AnyEntity):
-    """customized class for Bookmark entities"""
-    id = 'Bookmark'
-    fetch_attrs, fetch_order = fetch_config(['title', 'path'])
-
-    def actual_url(self):
-        url = self.req.build_url(self.path)
-        if self.title:
-            urlparts = list(urlsplit(url))
-            if urlparts[3]:
-                urlparts[3] += '&vtitle=%s' % self.req.url_quote(self.title)
-            else:
-                urlparts[3] = 'vtitle=%s' % self.req.url_quote(self.title)
-            url = urlunsplit(urlparts)
-        return url
-
-    def action_url(self):
-        return self.absolute_url() + '/follow'
-
-
 class CWCache(AnyEntity):
     """Cache"""
     id = 'CWCache'
--- a/entities/test/unittest_base.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/entities/test/unittest_base.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,16 +11,15 @@
 from logilab.common.decorators import clear_cache
 from logilab.common.interface import implements
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb import ValidationError
 from cubicweb.interfaces import IMileStone, IWorkflowable
 from cubicweb.entities import AnyEntity
 from cubicweb.entities.authobjs import CWUser
-from cubicweb.web.widgets import AutoCompletionWidget
 
 
-class BaseEntityTC(EnvBasedTC):
+class BaseEntityTC(CubicWebTC):
 
     def setup_database(self):
         self.member = self.create_user('member')
@@ -260,7 +259,7 @@
         self.assertEquals(e.latest_trinfo().comment, 'deactivate 2')
 
 
-class InterfaceTC(EnvBasedTC):
+class InterfaceTC(CubicWebTC):
 
     def test_nonregr_subclasses_and_mixins_interfaces(self):
         self.failUnless(implements(CWUser, IWorkflowable))
@@ -275,7 +274,7 @@
         self.failUnless(implements(MyUser_, IWorkflowable))
 
 
-class SpecializedEntityClassesTC(EnvBasedTC):
+class SpecializedEntityClassesTC(CubicWebTC):
 
     def select_eclass(self, etype):
         # clear selector cache
--- a/entity.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/entity.py	Fri Aug 14 00:02:08 2009 +0200
@@ -12,7 +12,6 @@
 from logilab.common import interface
 from logilab.common.compat import all
 from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated
 from logilab.mtconverter import TransformData, TransformError, xml_escape
 
 from rql.utils import rqlvar_maker
@@ -21,7 +20,7 @@
 from cubicweb.rset import ResultSet
 from cubicweb.selectors import yes
 from cubicweb.appobject import AppObject
-from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype
+from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint
 
 from cubicweb.common.uilib import printable_value, soup2xhtml
 from cubicweb.common.mixins import MI_REL_TRIGGERS
@@ -38,100 +37,6 @@
     return '1'
 
 
-_MODE_TAGS = set(('link', 'create'))
-_CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
-
-try:
-    from cubicweb.web import formwidgets, uicfg
-
-    def _dispatch_rtags(tags, rtype, role, stype, otype):
-        for tag in tags:
-            if tag in _MODE_TAGS:
-                uicfg.actionbox_appearsin_addmenu.tag_relation(
-                    (stype, rtype, otype, role), tag == 'create')
-            elif tag in _CATEGORY_TAGS:
-                uicfg.autoform_section.tag_relation((stype, rtype, otype, role),
-                                                    tag)
-            elif tag == 'inlineview':
-                uicfg.autoform_is_inlined.tag_relation((stype, rtype, otype, role), True)
-            else:
-                raise ValueError(tag)
-
-except ImportError:
-
-    _dispatch_rtags = None
-
-def _get_etype(bases, classdict):
-    try:
-        return classdict['id']
-    except KeyError:
-        for base in bases:
-            etype = getattr(base, 'id', None)
-            if etype and etype != 'Any':
-                return etype
-
-def _get_defs(attr, name, bases, classdict):
-    try:
-        yield name, classdict.pop(attr)
-    except KeyError:
-        for base in bases:
-            try:
-                value = getattr(base, attr)
-                delattr(base, attr)
-                yield base.__name__, value
-            except AttributeError:
-                continue
-
-class _metaentity(type):
-    """this metaclass sets the relation tags on the entity class
-    and deals with the `widgets` attribute
-    """
-    def __new__(mcs, name, bases, classdict):
-        # collect baseclass' rtags
-        etype = _get_etype(bases, classdict)
-        if etype and _dispatch_rtags is not None:
-            for name, rtags in _get_defs('__rtags__', name, bases, classdict):
-                warn('%s: __rtags__ is deprecated' % name, DeprecationWarning)
-                for relation, tags in rtags.iteritems():
-                    # tags must become an iterable
-                    if isinstance(tags, basestring):
-                        tags = (tags,)
-                    # relation must become a 3-uple (rtype, targettype, role)
-                    if isinstance(relation, basestring):
-                        _dispatch_rtags(tags, relation, 'subject', etype, '*')
-                        _dispatch_rtags(tags, relation, 'object', '*', etype)
-                    elif len(relation) == 1: # useful ?
-                        _dispatch_rtags(tags, relation[0], 'subject', etype, '*')
-                        _dispatch_rtags(tags, relation[0], 'object', '*', etype)
-                    elif len(relation) == 2:
-                        rtype, ttype = relation
-                        ttype = bw_normalize_etype(ttype) # XXX bw compat
-                        _dispatch_rtags(tags, rtype, 'subject', etype, ttype)
-                        _dispatch_rtags(tags, rtype, 'object', ttype, etype)
-                    elif len(relation) == 3:
-                        rtype, ttype, role = relation
-                        ttype = bw_normalize_etype(ttype)
-                        if role == 'subject':
-                            _dispatch_rtags(tags, rtype, 'subject', etype, ttype)
-                        else:
-                            _dispatch_rtags(tags, rtype, 'object', ttype, etype)
-                    else:
-                        raise ValueError('bad rtag definition (%r)' % (relation,))
-            for name, widgets in _get_defs('widgets', name, bases, classdict):
-                warn('%s: widgets is deprecated' % name, DeprecationWarning)
-                for rtype, wdgname in widgets.iteritems():
-                    if wdgname in ('URLWidget', 'EmbededURLWidget', 'RawDynamicComboBoxWidget'):
-                        warn('%s widget is deprecated' % wdgname, DeprecationWarning)
-                        continue
-                    if wdgname == 'StringWidget':
-                        wdgname = 'TextInput'
-                    widget = getattr(formwidgets, wdgname)
-                    assert hasattr(widget, 'render')
-                    uicfg.autoform_field_kwargs.tag_subject_of(
-                        (etype, rtype, '*'), {'widget': widget})
-        return super(_metaentity, mcs).__new__(mcs, name, bases, classdict)
-
-
 class Entity(AppObject, dict):
     """an entity instance has e_schema automagically set on
     the class and instances has access to their issuing cursor.
@@ -155,28 +60,24 @@
                          as composite relations or relations that have '?1' as object
                          cardinality
     """
-    __metaclass__ = _metaentity
     __registry__ = 'etypes'
     __select__ = yes()
 
     # class attributes that must be set in class definition
-    id = None
     rest_attr = None
     fetch_attrs = None
     skip_copy_for = ()
     # class attributes set automatically at registration time
     e_schema = None
 
-    MODE_TAGS = set(('link', 'create'))
-    CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
     @classmethod
-    def __initialize__(cls):
+    def __initialize__(cls, schema):
         """initialize a specific entity class by adding descriptors to access
         entity type's attributes and relations
         """
-        etype = cls.id
+        etype = cls.__id__
         assert etype != 'Any', etype
-        cls.e_schema = eschema = cls.schema.eschema(etype)
+        cls.e_schema = eschema = schema.eschema(etype)
         for rschema, _ in eschema.attribute_definitions():
             if rschema.type == 'eid':
                 continue
@@ -205,7 +106,7 @@
         """return a rql to fetch all entities of the class type"""
         restrictions = restriction or []
         if settype:
-            restrictions.append('%s is %s' % (mainvar, cls.id))
+            restrictions.append('%s is %s' % (mainvar, cls.__id__))
         if fetchattrs is None:
             fetchattrs = cls.fetch_attrs
         selection = [mainvar]
@@ -238,7 +139,7 @@
                 rschema = eschema.subject_relation(attr)
             except KeyError:
                 cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
-                            attr, cls.id)
+                            attr, cls.__id__)
                 continue
             if not user.matching_groups(rschema.get_groups('read')):
                 continue
@@ -256,7 +157,8 @@
                     continue
                 if card == '?':
                     restrictions[-1] += '?' # left outer join if not mandatory
-                destcls = cls.vreg['etypes'].etype_class(desttype)
+                # XXX user.req.vreg iiiirk
+                destcls = user.req.vreg['etypes'].etype_class(desttype)
                 destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs,
                                             selection, orderby, restrictions,
                                             user, ordermethod, visited=visited)
@@ -267,14 +169,6 @@
 
     @classmethod
     @cached
-    def parent_classes(cls):
-        parents = [cls.vreg['etypes'].etype_class(e.type)
-                   for e in cls.e_schema.ancestors()]
-        parents.append(cls.vreg['etypes'].etype_class('Any'))
-        return parents
-
-    @classmethod
-    @cached
     def _rest_attr_info(cls):
         mainattr, needcheck = 'eid', True
         if cls.rest_attr:
@@ -291,7 +185,7 @@
         return mainattr, needcheck
 
     def __init__(self, req, rset=None, row=None, col=0):
-        AppObject.__init__(self, req, rset, row, col)
+        AppObject.__init__(self, req, rset=rset, row=row, col=col)
         dict.__init__(self)
         self._related_cache = {}
         if rset is not None:
@@ -380,11 +274,11 @@
                 kwargs['_restpath'] = self.rest_path(kwargs.get('base_url'))
             except TypeError:
                 warn('%s: rest_path() now take use_ext_eid argument, '
-                     'please update' % self.id, DeprecationWarning)
+                     'please update' % self.__id__, DeprecationWarning)
                 kwargs['_restpath'] = self.rest_path()
         else:
             kwargs['rql'] = 'Any X WHERE X eid %s' % self.eid
-        return self.build_url(method, **kwargs)
+        return self.req.build_url(method, **kwargs)
 
     def rest_path(self, use_ext_eid=False):
         """returns a REST-like (relative) path for this entity"""
@@ -522,7 +416,7 @@
     def as_rset(self):
         """returns a resultset containing `self` information"""
         rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s',
-                         {'x': self.eid}, [(self.id,)])
+                         {'x': self.eid}, [(self.__id__,)])
         return self.req.decorate_rset(rset)
 
     def to_complete_relations(self):
@@ -682,7 +576,7 @@
         return self.related(rtype, role, limit, entities)
 
     def related_rql(self, rtype, role='subject'):
-        rschema = self.schema[rtype]
+        rschema = self.req.vreg.schema[rtype]
         if role == 'subject':
             targettypes = rschema.objects(self.e_schema)
             restriction = 'E eid %%(x)s, E %s X' % rtype
@@ -715,20 +609,6 @@
 
     # generic vocabulary methods ##############################################
 
-    @deprecated('see new form api')
-    def vocabulary(self, rtype, role='subject', limit=None):
-        """vocabulary functions must return a list of couples
-        (label, eid) that will typically be used to fill the
-        edition view's combobox.
-
-        If `eid` is None in one of these couples, it should be
-        interpreted as a separator in case vocabulary results are grouped
-        """
-        from logilab.common.testlib import mock_object
-        form = self.vreg.select('forms', 'edition', self.req, entity=self)
-        field = mock_object(name=rtype, role=role)
-        return form.form_field_vocabulary(field, limit)
-
     def unrelated_rql(self, rtype, targettype, role, ordermethod=None,
                       vocabconstraints=True):
         """build a rql to fetch `targettype` entities unrelated to this entity
@@ -736,7 +616,7 @@
         """
         ordermethod = ordermethod or 'fetch_unrelated_order'
         if isinstance(rtype, basestring):
-            rtype = self.schema.rschema(rtype)
+            rtype = self.req.vreg.schema.rschema(rtype)
         if role == 'subject':
             evar, searchedvar = 'S', 'O'
             subjtype, objtype = self.e_schema, targettype
@@ -801,7 +681,7 @@
         """set cached values for the given relation"""
         if rset:
             related = list(rset.entities(col))
-            rschema = self.schema.rschema(rtype)
+            rschema = self.req.vreg.schema.rschema(rtype)
             if role == 'subject':
                 rcard = rschema.rproperty(self.e_schema, related[0].e_schema,
                                           'cardinality')[1]
@@ -941,7 +821,7 @@
     def __set__(self, eobj, value):
         # XXX bw compat
         # would be better to generate UPDATE queries than the current behaviour
-        eobj.warning("deprecated usage, don't use 'entity.attr = val' notation)")
+        eobj.warning("[3.4] deprecated usage, don't use 'entity.attr = val' notation)")
         eobj[self._attrname] = value
 
 
--- a/etwist/server.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/etwist/server.py	Fri Aug 14 00:02:08 2009 +0200
@@ -116,7 +116,7 @@
         start_task(interval, self.appli.session_handler.clean_sessions)
 
     def set_url_rewriter(self):
-        self.url_rewriter = self.appli.vreg['components'].select_object('urlrewriter')
+        self.url_rewriter = self.appli.vreg['components'].select_or_none('urlrewriter')
 
     def shutdown_event(self):
         """callback fired when the server is shutting down to properly
--- a/etwist/test/unittest_server.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/etwist/test/unittest_server.py	Fri Aug 14 00:02:08 2009 +0200
@@ -5,11 +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 cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.etwist.server import host_prefixed_baseurl
 
 
-class HostPrefixedBaseURLTC(EnvBasedTC):
+class HostPrefixedBaseURLTC(CubicWebTC):
 
     def _check(self, baseurl, host, waited):
         self.assertEquals(host_prefixed_baseurl(baseurl, host), waited,
--- a/etwist/twctl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/etwist/twctl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -8,7 +8,6 @@
 
 import sys
 
-from cubicweb import underline_title
 from cubicweb.toolsutils import CommandHandler
 
 # trigger configuration registration
--- a/ext/test/unittest_rest.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/ext/test/unittest_rest.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,11 +6,11 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.ext.rest import rest_publish
 
-class RestTC(EnvBasedTC):
+class RestTC(CubicWebTC):
     def context(self):
         return self.execute('CWUser X WHERE X login "admin"').get_entity(0, 0)
 
--- a/goa/appobjects/components.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/appobjects/components.py	Fri Aug 14 00:02:08 2009 +0200
@@ -28,7 +28,7 @@
     __select__ = one_line_rset() & match_search_state('linksearch') & accept
 
     def cell_call(self, row, col):
-        entity = self.entity(0, 0)
+        entity = self.rset.get_entity(0, 0)
         role, eid, rtype, etype = self.req.search_state[1]
         assert entity.eid == typed_eid(eid)
         rset = entity.unrelated(rtype, etype, role, ordermethod='fetch_order')
--- a/goa/db.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/db.py	Fri Aug 14 00:02:08 2009 +0200
@@ -35,7 +35,8 @@
 
 from logilab.common.decorators import cached, iclassmethod
 
-from cubicweb import RequestSessionMixIn, Binary, entities
+from cubicweb import Binary, entities
+from cubicweb.req import RequestSessionBase
 from cubicweb.rset import ResultSet
 from cubicweb.entity import metaentity
 from cubicweb.server.utils import crypt_password
@@ -92,7 +93,7 @@
 def needrequest(wrapped):
     def wrapper(cls, *args, **kwargs):
         req = kwargs.pop('req', None)
-        if req is None and args and isinstance(args[0], RequestSessionMixIn):
+        if req is None and args and isinstance(args[0], RequestSessionBase):
             args = list(args)
             req = args.pop(0)
         if req is None:
@@ -155,7 +156,7 @@
         #
         # Entity prototype:
         #   __init__(self, req, rset, row=None, col=0)
-        if args and isinstance(args[0], RequestSessionMixIn) or 'req' in kwargs:
+        if args and isinstance(args[0], RequestSessionBase) or 'req' in kwargs:
             super(Model, self).__init__(*args, **kwargs)
             self._gaeinitargs = None
         else:
--- a/goa/doc/quickstart.txt	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/doc/quickstart.txt	Fri Aug 14 00:02:08 2009 +0200
@@ -1,3 +1,5 @@
+.. -*- coding: utf-8 -*-
+
 Introduction
 =============
 
@@ -11,7 +13,7 @@
   application.
 
 *result set*
-  objet qui encaspule les résultats d'une requête adressée à l'entrepôt 
+  objet qui encaspule les résultats d'une requête adressée à l'entrepôt
   de données et des informations sur cette requête.
 
 *vue*
@@ -23,8 +25,8 @@
 Définition d'une application de Blog
 ====================================
 
-La première chose à faire est de copier le squelette depuis le répertoire 
-``lax/skel`` vers un nouveau répertoire qui sera votre application 
+La première chose à faire est de copier le squelette depuis le répertoire
+``lax/skel`` vers un nouveau répertoire qui sera votre application
 ``Google AppEngine``::
 
   $ cp -r lax/skel myapp
@@ -36,7 +38,7 @@
 données manipulées. La syntaxe de la définition est la même que celle
 proposée par `Google AppEngine`_ mais il faut remplacer la ligne
 d'import::
-  
+
   from google.appengine.ext import db
 
 par celle-ci::
@@ -47,7 +49,7 @@
 Un exemple de schéma de données pour un ``Blog`` pourrait être::
 
   from cubicweb.goa import db
-  
+
   class Blog(db.Model):
       # un titre à donner à l'entrée
       title = db.StringProperty(required=True)
@@ -56,15 +58,15 @@
       # le contenu de l'entrée
       content = db.TextProperty()
       # une entrée peut en citer une autre
-      cites = db.SelfReferenceProperty() 
-      
+      cites = db.SelfReferenceProperty()
+
 
 Personnalisation des vues
 -------------------------
 
 ``LAX`` permet de générer directement, à partir de la définition
-du schéma, des vues de consultation, d'ajout et de modification 
-pour tous les types de donées manipulés. Il est toutefois 
+du schéma, des vues de consultation, d'ajout et de modification
+pour tous les types de donées manipulés. Il est toutefois
 généralement souhaitable de personnaliser les vues de consultations.
 
 Dans ``LAX``, les vues sont représentées par des classes Python.
@@ -74,7 +76,7 @@
 - un identifiant (tous les objets dans ``LAX`` sont enregistrés
   dans un registre et cet identifiant sert de clé pour y retrouver
   la vue)
-  
+
 - une description des types de données auxquels elle s'applique
 
 Il existe dans ``LAX`` des vues prédéfinies et utilisées par le moteur
@@ -86,17 +88,17 @@
 Par exemple, si on souhaite modifier la page principale d'une entrée de
 blog, il faut surcharger la vue ``primary`` des objets ``Blog`` dans
 le fichier ``myapp/views.py``::
-  
+
   from cubicweb.web.views import baseviews
-  
+
   class BlogPrimaryView(baseviews.PrimaryView):
       accepts = ('Blog',)
-      
+
       def cell_call(self, row, col):
-          entity = self.entity(row, col)
+          entity = self.rset.get_entity(row, col)
           self.w(u'<h1>%s</h1>' % entity.title)
           self.w(u'<div>%s</div>' entity.content)
-    
+
 
 Génération du graphique de schéma
 ---------------------------------
@@ -104,13 +106,13 @@
 Il existe une vue ``schema`` qui permet d'afficher un graphique
 représantant les différents types d'entités définis dans le schéma
 ainsi que les relations entre ces types. Ce graphique doit être généré
-statiquement. Le script à utiliser pour générer ce schéma est 
+statiquement. Le script à utiliser pour générer ce schéma est
 dans ``myapp/tools``. Ce script nécessite d'avoir accès aux
 bibliothèques fournies par le SDK de ``Google AppEngine``. Il faut
 donc modifier son PYTHONPATH::
 
   $ export PYTHONPATH=GAE_ROOT/google:GAE_ROOT/lib/yaml
-  $ python tools/generate_schema_img.py 
+  $ python tools/generate_schema_img.py
 
 
 Génération des fichiers de traduction
--- a/goa/goactl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/goactl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -112,7 +112,6 @@
     'web/httpcache.py',
     'web/request.py',
     'web/webconfig.py',
-    'web/widgets.py',
 
     'web/views/__init__.py',
     'web/views/actions.py',
--- a/goa/skel/views.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/skel/views.py	Fri Aug 14 00:02:08 2009 +0200
@@ -19,7 +19,7 @@
     accepts = ('BlogEntry',)
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<h1>%s</h1>' % entity.dc_title())
         entity.view('metadata', w=self.w)
         self.w(entity.printable_value('text'))
--- a/goa/test/unittest_editcontroller.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/goa/test/unittest_editcontroller.py	Fri Aug 14 00:02:08 2009 +0200
@@ -401,11 +401,11 @@
             #    which fires a Redirect
             # 2/ When re-publishing the copy form, the publisher implicitly commits
             try:
-                self.env.app.publish('edit', self.req)
+                self.app.publish('edit', self.req)
             except Redirect:
                 self.req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                 self.req.form['vid'] = 'copy'
-                self.env.app.publish('view', self.req)
+                self.app.publish('view', self.req)
             rset = self.req.execute('CWUser P WHERE P surname "Boom"')
             self.assertEquals(len(rset), 0)
         finally:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/req.py	Fri Aug 14 00:02:08 2009 +0200
@@ -0,0 +1,311 @@
+"""Base class for request/session
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: Library General Public License version 2 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+
+from urllib import quote as urlquote, unquote as urlunquote
+from datetime import time, datetime, timedelta
+
+from logilab.common.decorators import cached
+
+from cubicweb import Unauthorized, typed_eid
+from cubicweb.rset import ResultSet
+from cubicweb.utils import ustrftime, strptime, todate, todatetime
+
+ONESECOND = timedelta(0, 1, 0)
+CACHE_REGISTRY = {}
+
+
+class Cache(dict):
+    def __init__(self):
+        super(Cache, self).__init__()
+        _now = datetime.now()
+        self.cache_creation_date = _now
+        self.latest_cache_lookup = _now
+
+
+class RequestSessionBase(object):
+    """base class containing stuff shared by server session and web request
+
+    request/session is the main resources accessor, mainly through it's vreg
+    attribute:
+    :vreg:
+      the instance's registry
+    :vreg.schema:
+      the instance's schema
+    :vreg.config:
+      the instance's configuration
+    """
+    def __init__(self, vreg):
+        self.vreg = vreg
+        try:
+            encoding = vreg.property_value('ui.encoding')
+        except: # no vreg or property not registered
+            encoding = 'utf-8'
+        self.encoding = encoding
+        # cache result of execution for (rql expr / eids),
+        # should be emptied on commit/rollback of the server session / web
+        # connection
+        self.local_perm_cache = {}
+        self._ = unicode
+
+    def property_value(self, key):
+        """return value of the property with the given key, giving priority to
+        user specific value if any, else using site value
+        """
+        if self.user:
+            return self.user.property_value(key)
+        return self.vreg.property_value(key)
+
+    def etype_rset(self, etype, size=1):
+        """return a fake result set for a particular entity type"""
+        rset = ResultSet([('A',)]*size, '%s X' % etype,
+                         description=[(etype,)]*size)
+        def get_entity(row, col=0, etype=etype, req=self, rset=rset):
+            return req.vreg.etype_class(etype)(req, rset, row, col)
+        rset.get_entity = get_entity
+        return self.decorate_rset(rset)
+
+    def eid_rset(self, eid, etype=None):
+        """return a result set for the given eid without doing actual query
+        (we have the eid, we can suppose it exists and user has access to the
+        entity)
+        """
+        eid = typed_eid(eid)
+        if etype is None:
+            etype = self.describe(eid)[0]
+        rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
+                         [(etype,)])
+        return self.decorate_rset(rset)
+
+    def empty_rset(self):
+        """return a result set for the given eid without doing actual query
+        (we have the eid, we can suppose it exists and user has access to the
+        entity)
+        """
+        return self.decorate_rset(ResultSet([], 'Any X WHERE X eid -1'))
+
+    def entity_from_eid(self, eid, etype=None):
+        """return an entity instance for the given eid. No query is done"""
+        try:
+            return self.entity_cache(eid)
+        except KeyError:
+            rset = self.eid_rset(eid, etype)
+            entity = rset.get_entity(0, 0)
+            self.set_entity_cache(entity)
+            return entity
+
+    def entity_cache(self, eid):
+        raise KeyError
+
+    def set_entity_cache(self, entity):
+        pass
+
+    def ensure_ro_rql(self, rql):
+        """raise an exception if the given rql is not a select query"""
+        first = rql.split(' ', 1)[0].lower()
+        if first in ('insert', 'set', 'delete'):
+            raise Unauthorized(self._('only select queries are authorized'))
+
+    def get_cache(self, cachename):
+        """
+        NOTE: cachename should be dotted names as in :
+        - cubicweb.mycache
+        - cubes.blog.mycache
+        - etc.
+        """
+        if cachename in CACHE_REGISTRY:
+            cache = CACHE_REGISTRY[cachename]
+        else:
+            cache = CACHE_REGISTRY[cachename] = Cache()
+        _now = datetime.now()
+        if _now > cache.latest_cache_lookup + ONESECOND:
+            ecache = self.execute(
+                'Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T',
+                {'name':cachename}).get_entity(0,0)
+            cache.latest_cache_lookup = _now
+            if not ecache.valid(cache.cache_creation_date):
+                cache.clear()
+                cache.cache_creation_date = _now
+        return cache
+
+    # url generation methods ##################################################
+
+    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:
+            if self.from_controller() == 'view' and not '_restpath' in kwargs:
+                method = self.relative_path(includeparams=False) or 'view'
+            else:
+                method = 'view'
+        base_url = kwargs.pop('base_url', None)
+        if base_url is None:
+            base_url = self.base_url()
+        if '_restpath' in kwargs:
+            assert method == 'view', method
+            path = kwargs.pop('_restpath')
+        else:
+            path = method
+        if not kwargs:
+            return u'%s%s' % (base_url, path)
+        return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
+
+
+    def build_url_params(self, **kwargs):
+        """return encoded params to incorporate them in an URL"""
+        args = []
+        for param, values in kwargs.items():
+            if not isinstance(values, (list, tuple)):
+                values = (values,)
+            for value in values:
+                args.append(u'%s=%s' % (param, self.url_quote(value)))
+        return '&'.join(args)
+
+    def url_quote(self, value, safe=''):
+        """urllib.quote is not unicode safe, use this method to do the
+        necessary encoding / decoding. Also it's designed to quote each
+        part of a url path and so the '/' character will be encoded as well.
+        """
+        if isinstance(value, unicode):
+            quoted = urlquote(value.encode(self.encoding), safe=safe)
+            return unicode(quoted, self.encoding)
+        return urlquote(str(value), safe=safe)
+
+    def url_unquote(self, quoted):
+        """returns a unicode unquoted string
+
+        decoding is based on `self.encoding` which is the encoding
+        used in `url_quote`
+        """
+        if isinstance(quoted, unicode):
+            quoted = quoted.encode(self.encoding)
+        try:
+            return unicode(urlunquote(quoted), self.encoding)
+        except UnicodeDecodeError: # might occurs on manually typed URLs
+            return unicode(urlunquote(quoted), 'iso-8859-1')
+
+    # bound user related methods ###############################################
+
+    @cached
+    def user_data(self):
+        """returns a dictionnary with this user's information"""
+        userinfo = {}
+        if self.is_internal_session:
+            userinfo['login'] = "cubicweb"
+            userinfo['name'] = "cubicweb"
+            userinfo['email'] = ""
+            return userinfo
+        user = self.actual_session().user
+        rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
+        try:
+            firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
+            if firstname is None and lastname is None:
+                userinfo['name'] = ''
+            else:
+                userinfo['name'] = ("%s %s" % (firstname, lastname))
+            userinfo['email'] = email
+        except IndexError:
+            userinfo['name'] = None
+            userinfo['email'] = None
+        userinfo['login'] = user.login
+        return userinfo
+
+    def is_internal_session(self):
+        """overrided on the server-side"""
+        return False
+
+    # formating methods #######################################################
+
+    def view(self, __vid, rset=None, __fallback_oid=None, __registry='views',
+             **kwargs):
+        """shortcut to self.vreg.view method avoiding to pass the request"""
+        return self.vreg[__registry].render(__vid, self, __fallback_oid,
+                                            rset=rset, **kwargs)
+
+    def format_date(self, date, date_format=None, time=False):
+        """return a string for a date time according to instance's
+        configuration
+        """
+        if date:
+            if date_format is None:
+                if time:
+                    date_format = self.property_value('ui.datetime-format')
+                else:
+                    date_format = self.property_value('ui.date-format')
+            return ustrftime(date, date_format)
+        return u''
+
+    def format_time(self, time):
+        """return a string for a time according to instance's
+        configuration
+        """
+        if time:
+            return ustrftime(time, self.property_value('ui.time-format'))
+        return u''
+
+    def format_float(self, num):
+        """return a string for floating point number according to instance's
+        configuration
+        """
+        if num:
+            return self.property_value('ui.float-format') % num
+        return u''
+
+    def parse_datetime(self, value, etype='Datetime'):
+        """get a datetime or time from a string (according to etype)
+        Datetime formatted as Date are accepted
+        """
+        assert etype in ('Datetime', 'Date', 'Time'), etype
+        # XXX raise proper validation error
+        if etype == 'Datetime':
+            format = self.property_value('ui.datetime-format')
+            try:
+                return todatetime(strptime(value, format))
+            except ValueError:
+                pass
+        elif etype == 'Time':
+            format = self.property_value('ui.time-format')
+            try:
+                # (adim) I can't find a way to parse a Time with a custom format
+                date = strptime(value, format) # this returns a DateTime
+                return time(date.hour, date.minute, date.second)
+            except ValueError:
+                raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+        try:
+            format = self.property_value('ui.date-format')
+            dt = strptime(value, format)
+            if etype == 'Datetime':
+                return todatetime(dt)
+            return todate(dt)
+        except ValueError:
+            raise ValueError('can\'t parse %r (expected %s)' % (value, format))
+
+    # abstract methods to override according to the web front-end #############
+
+    def base_url(self):
+        """return the root url of the instance"""
+        raise NotImplementedError
+
+    def decorate_rset(self, rset):
+        """add vreg/req (at least) attributes to the given result set """
+        raise NotImplementedError
+
+    def describe(self, eid):
+        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
+        raise NotImplementedError
--- a/rset.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/rset.py	Fri Aug 14 00:02:08 2009 +0200
@@ -9,7 +9,7 @@
 
 from logilab.common.decorators import cached, clear_cache, copy_cache
 
-from rql import nodes
+from rql import nodes, stmts
 
 from cubicweb import NotAnEntity
 
@@ -83,7 +83,7 @@
         try:
             return self._rsetactions[key]
         except KeyError:
-            actions = self.vreg['actions'].possible_vobjects(
+            actions = self.vreg['actions'].poss_visible_objects(
                 self.req, rset=self, **kwargs)
             self._rsetactions[key] = actions
             return actions
@@ -240,8 +240,6 @@
                 rset = mapping[key]
             rset.rows.append(self.rows[idx])
             rset.description.append(self.description[idx])
-
-
         for rset in result:
             rset.rowcount = len(rset.rows)
         if return_dict:
@@ -249,6 +247,51 @@
         else:
             return result
 
+    def limited_rql(self):
+        """return a printable rql for the result set associated to the object,
+        with limit/offset correctly set according to maximum page size and
+        currently displayed page when necessary
+        """
+        # 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['components'].select_or_none('navigation', self.req,
+                                                     rset=self)
+        if nav:
+            start, stop = nav.page_boundaries()
+            rql = self._limit_offset_rql(stop - start, start)
+        # result set may have be limited manually in which case navigation won't
+        # apply
+        elif self.limited:
+            rql = self._limit_offset_rql(*self.limited)
+        # navigation component doesn't apply and rset has not been limited, no
+        # need to limit query
+        else:
+            rql = self.printable_rql()
+        return rql
+
+    def _limit_offset_rql(self, limit, offset):
+        rqlst = self.syntax_tree()
+        if len(rqlst.children) == 1:
+            select = rqlst.children[0]
+            olimit, ooffset = select.limit, select.offset
+            select.limit, select.offset = limit, offset
+            rql = rqlst.as_string(kwargs=self.args)
+            # restore original limit/offset
+            select.limit, select.offset = olimit, ooffset
+        else:
+            newselect = stmts.Select()
+            newselect.limit = limit
+            newselect.offset = offset
+            aliases = [nodes.VariableRef(newselect.get_variable(vref.name, i))
+                       for i, vref in enumerate(rqlst.selection)]
+            newselect.set_with([nodes.SubQuery(aliases, rqlst)], check=False)
+            newunion = stmts.Union()
+            newunion.append(newselect)
+            rql = rqlst.as_string(kwargs=self.args)
+            rqlst.parent = None
+        return rql
+
     def limit(self, limit, offset=0, inplace=False):
         """limit the result set to the given number of rows optionaly starting
         from an index different than 0
@@ -318,6 +361,14 @@
             if self.rows[i][col] is not None:
                 yield self.get_entity(i, col)
 
+    def complete_entity(self, row, col=0, skip_bytes=True):
+        """short cut to get an completed entity instance for a particular
+        row (all instance's attributes have been fetched)
+        """
+        entity = self.get_entity(row, col)
+        entity.complete(skip_bytes=skip_bytes)
+        return entity
+
     @cached
     def get_entity(self, row, col=None):
         """special method for query retreiving a single entity, returns a
--- a/schema.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/schema.py	Fri Aug 14 00:02:08 2009 +0200
@@ -79,7 +79,7 @@
     # ensure unicode
     # added .lower() in case no translation are available
     return unicode(req._(key)).lower()
-__builtins__['display_name'] = deprecated('display_name should be imported from cubicweb.schema')(display_name)
+__builtins__['display_name'] = deprecated('[3.4] display_name should be imported from cubicweb.schema')(display_name)
 
 def ERSchema_display_name(self, req, form=''):
     """return a internationalized string for the entity/relation type name in
@@ -949,5 +949,6 @@
 
 # XXX deprecated
 from yams.constraints import format_constraint
+format_constraint = deprecated('[3.4] use RichString instead of format_constraint')(format_constraint)
 from yams.buildobjs import RichString
 PyFileReader.context['format_constraint'] = format_constraint
--- a/selectors.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/selectors.py	Fri Aug 14 00:02:08 2009 +0200
@@ -46,7 +46,6 @@
 from warnings import warn
 
 from logilab.common.compat import all
-from logilab.common.deprecation import deprecated
 from logilab.common.interface import implements as implements_iface
 
 from yams import BASE_TYPES
@@ -113,13 +112,13 @@
         return traceback is None
 
 
-def score_interface(cls_or_inst, cls, iface):
+def score_interface(etypesreg, cls_or_inst, cls, iface):
     """Return XXX if the give object (maybe an instance or class) implements
     the interface.
     """
     if getattr(iface, '__registry__', None) == 'etypes':
         # adjust score if the interface is an entity class
-        parents = cls_or_inst.parent_classes()
+        parents = etypesreg.parent_classes(cls_or_inst.id)
         if iface is cls:
             return len(parents) + 4
         if iface is parents[-1]: # Any
@@ -158,17 +157,18 @@
         return '%s(%s)' % (self.__class__.__name__,
                            ','.join(str(s) for s in self.expected_ifaces))
 
-    def score_interfaces(self, cls_or_inst, cls):
+    def score_interfaces(self, req, cls_or_inst, cls):
         score = 0
-        vreg, eschema = cls_or_inst.vreg, cls_or_inst.e_schema
+        etypesreg = req.vreg['etypes']
+        eschema = cls_or_inst.e_schema
         for iface in self.expected_ifaces:
             if isinstance(iface, basestring):
                 # entity type
                 try:
-                    iface = vreg['etypes'].etype_class(iface)
+                    iface = etypesreg.etype_class(iface)
                 except KeyError:
                     continue # entity type not in the schema
-            score += score_interface(cls_or_inst, cls, iface)
+            score += score_interface(etypesreg, cls_or_inst, cls, iface)
         return score
 
 
@@ -212,7 +212,7 @@
     def score(self, cls, req, etype):
         if etype in BASE_TYPES:
             return 0
-        return self.score_class(cls.vreg['etypes'].etype_class(etype), req)
+        return self.score_class(req.vreg['etypes'].etype_class(etype), req)
 
     def score_class(self, eclass, req):
         raise NotImplementedError()
@@ -561,7 +561,7 @@
 
     def __call__(self, cls, req, **kwargs):
         try:
-            cls.vreg[self.registry].select(self.oid, req, **kwargs)
+            req.vreg[self.registry].select(self.oid, req, **kwargs)
             return 1
         except NoSelectableObject:
             return 0
@@ -585,7 +585,7 @@
           proximity so the most specific object'll be selected
     """
     def score_class(self, eclass, req):
-        return self.score_interfaces(eclass, eclass)
+        return self.score_interfaces(req, eclass, eclass)
 
 
 class specified_etype_implements(implements):
@@ -615,11 +615,11 @@
             # only check this is a known type if etype comes from req.form,
             # else we want the error to propagate
             try:
-                etype = cls.vreg.case_insensitive_etypes[etype.lower()]
+                etype = req.vreg.case_insensitive_etypes[etype.lower()]
                 req.form['etype'] = etype
             except KeyError:
                 return 0
-        return self.score_class(cls.vreg['etypes'].etype_class(etype), req)
+        return self.score_class(req.vreg['etypes'].etype_class(etype), req)
 
 
 class entity_implements(ImplementsMixIn, EntitySelector):
@@ -638,7 +638,7 @@
           proximity so the most specific object'll be selected
     """
     def score_entity(self, entity):
-        return self.score_interfaces(entity, entity.__class__)
+        return self.score_interfaces(entity.req, entity, entity.__class__)
 
 
 class relation_possible(EClassSelector):
@@ -665,7 +665,7 @@
 
     @lltrace
     def __call__(self, cls, req, *args, **kwargs):
-        rschema = cls.schema.rschema(self.rtype)
+        rschema = req.vreg.schema.rschema(self.rtype)
         if not (rschema.has_perm(req, self.action)
                 or rschema.has_local_role(self.action)):
             return 0
@@ -854,7 +854,7 @@
         if row is None:
             score = 0
             need_local_check = []
-            geteschema = cls.schema.eschema
+            geteschema = req.vreg.schema.eschema
             for etype in rset.column_types(0):
                 if etype in BASE_TYPES:
                     return 0
@@ -892,7 +892,7 @@
     See `EClassSelector` documentation for behaviour when row is not specified.
     """
     def score(self, cls, req, etype):
-        eschema = cls.schema.eschema(etype)
+        eschema = req.vreg.schema.eschema(etype)
         if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
                and eschema.has_perm(req, 'add'):
             return 1
@@ -961,200 +961,3 @@
     def __init__(self, scorefunc, once_is_enough=False):
         super(score_entity, self).__init__(once_is_enough)
         self.score_entity = scorefunc
-
-
-# XXX DEPRECATED ##############################################################
-from cubicweb.vregistry import chainall
-
-yes_selector = deprecated()(yes)
-norset_selector = deprecated()(none_rset)
-rset_selector = deprecated()(any_rset)
-anyrset_selector = deprecated()(nonempty_rset)
-emptyrset_selector = deprecated()(empty_rset)
-onelinerset_selector = deprecated()(one_line_rset)
-twolinerset_selector = deprecated()(two_lines_rset)
-twocolrset_selector = deprecated()(two_cols_rset)
-largerset_selector = deprecated()(paginated_rset)
-sortedrset_selector = deprecated()(sorted_rset)
-oneetyperset_selector = deprecated()(one_etype_rset)
-multitype_selector = deprecated()(two_etypes_rset)
-anonymous_selector = deprecated()(anonymous_user)
-not_anonymous_selector = deprecated()(authenticated_user)
-primaryview_selector = deprecated()(primary_view)
-contextprop_selector = deprecated()(match_context_prop)
-
-@deprecated('use non_final_entity instead of %s')
-def nfentity_selector(cls, req, rset=None, row=None, col=0, **kwargs):
-    return non_final_entity()(cls, req, rset, row, col)
-
-@deprecated('use implements instead of %s')
-def implement_interface(cls, req, rset=None, row=None, col=0, **kwargs):
-    return implements(*cls.accepts_interfaces)(cls, req, rset, row, col)
-_interface_selector = deprecated()(implement_interface)
-interface_selector = deprecated()(implement_interface)
-
-@deprecated('use specified_etype_implements instead of %s')
-def accept_etype(cls, req, *args, **kwargs):
-    """check etype presence in request form *and* accepts conformance"""
-    return specified_etype_implements(*cls.accepts)(cls, req, *args)
-etype_form_selector = accept_etype
-
-@deprecated('use match_search_state instead of %s')
-def searchstate_selector(cls, req, rset=None, row=None, col=0, **kwargs):
-    return match_search_state(cls.search_states)(cls, req, rset, row, col)
-
-@deprecated('use match_user_groups instead of %s')
-def match_user_group(cls, req, rset=None, row=None, col=0, **kwargs):
-    return match_user_groups(*cls.require_groups)(cls, req, rset, row, col, **kwargs)
-in_group_selector = match_user_group
-
-@deprecated('use relation_possible instead of %s')
-def has_relation(cls, req, rset=None, row=None, col=0, **kwargs):
-    return relation_possible(cls.rtype, role(cls), cls.etype,
-                             getattr(cls, 'require_permission', 'read'))(cls, req, rset, row, col, **kwargs)
-
-@deprecated('use relation_possible instead of %s')
-def one_has_relation(cls, req, rset=None, row=None, col=0, **kwargs):
-    return relation_possible(cls.rtype, role(cls), cls.etype,
-                             getattr(cls, 'require_permission', 'read',
-                                     once_is_enough=True))(cls, req, rset, row, col, **kwargs)
-
-@deprecated('use implements instead of %s')
-def accept_rset(cls, req, rset=None, row=None, col=0, **kwargs):
-    """simply delegate to cls.accept_rset method"""
-    return implements(*cls.accepts)(cls, req, rset, row=row, col=col)
-accept_rset_selector = accept_rset
-
-accept = chainall(non_final_entity(), accept_rset, name='accept')
-accept = deprecated('use implements selector')(accept)
-accept_selector = deprecated()(accept)
-
-accept_one = deprecated()(chainall(one_line_rset, accept,
-                                   name='accept_one'))
-accept_one_selector = deprecated()(accept_one)
-
-
-def _rql_condition(cls, req, rset=None, row=None, col=0, **kwargs):
-    if cls.condition:
-        return rql_condition(cls.condition)(cls, req, rset, row, col)
-    return 1
-_rqlcondition_selector = deprecated()(_rql_condition)
-
-rqlcondition_selector = deprecated()(chainall(non_final_entity(), one_line_rset, _rql_condition,
-                         name='rql_condition'))
-
-@deprecated('use but_etype instead of %s')
-def but_etype_selector(cls, req, rset=None, row=None, col=0, **kwargs):
-    return but_etype(cls.etype)(cls, req, rset, row, col)
-
-@lltrace
-def etype_rtype_selector(cls, req, rset=None, row=None, col=0, **kwargs):
-    schema = cls.schema
-    perm = getattr(cls, 'require_permission', 'read')
-    if hasattr(cls, 'etype'):
-        eschema = schema.eschema(cls.etype)
-        if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)):
-            return 0
-    if hasattr(cls, 'rtype'):
-        rschema = schema.rschema(cls.rtype)
-        if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)):
-            return 0
-    return 1
-etype_rtype_selector = deprecated()(etype_rtype_selector)
-
-#req_form_params_selector = deprecated()(match_form_params) # form_params
-#kwargs_selector = deprecated()(match_kwargs) # expected_kwargs
-
-# compound selectors ##########################################################
-
-searchstate_accept = chainall(nonempty_rset(), accept,
-                              name='searchstate_accept')
-searchstate_accept_selector = deprecated()(searchstate_accept)
-
-searchstate_accept_one = chainall(one_line_rset, accept, _rql_condition,
-                                  name='searchstate_accept_one')
-searchstate_accept_one_selector = deprecated()(searchstate_accept_one)
-
-searchstate_accept = deprecated()(searchstate_accept)
-searchstate_accept_one = deprecated()(searchstate_accept_one)
-
-# end of deprecation section ##################################################
-
-def unbind_method(selector):
-    def new_selector(registered):
-        # get the unbound method
-        if hasattr(registered, 'im_func'):
-            registered = registered.im_func
-        # don't rebind since it will be done automatically during
-        # the assignment, inside the destination class body
-        return selector(registered)
-    new_selector.__name__ = selector.__name__
-    return new_selector
-
-
-def deprecate(registered, msg):
-    # get the unbound method
-    if hasattr(registered, 'im_func'):
-        registered = registered.im_func
-    def _deprecate(cls, vreg):
-        warn(msg, DeprecationWarning)
-        return registered(cls, vreg)
-    return _deprecate
-
-@unbind_method
-def require_group_compat(registered):
-    def plug_selector(cls, vreg):
-        cls = registered(cls, vreg)
-        if getattr(cls, 'require_groups', None):
-            warn('use "match_user_groups(group1, group2)" instead of using require_groups',
-                 DeprecationWarning)
-            cls.__select__ &= match_user_groups(cls.require_groups)
-        return cls
-    return plug_selector
-
-@unbind_method
-def accepts_compat(registered):
-    def plug_selector(cls, vreg):
-        cls = registered(cls, vreg)
-        if getattr(cls, 'accepts', None):
-            warn('use "implements("EntityType", IFace)" instead of using accepts on %s'
-                 % cls,
-                 DeprecationWarning)
-            cls.__select__ &= implements(*cls.accepts)
-        return cls
-    return plug_selector
-
-@unbind_method
-def accepts_etype_compat(registered):
-    def plug_selector(cls, vreg):
-        cls = registered(cls, vreg)
-        if getattr(cls, 'accepts', None):
-            warn('use "specified_etype_implements("EntityType", IFace)" instead of using accepts',
-                 DeprecationWarning)
-            cls.__select__ &= specified_etype_implements(*cls.accepts)
-        return cls
-    return plug_selector
-
-@unbind_method
-def condition_compat(registered):
-    def plug_selector(cls, vreg):
-        cls = registered(cls, vreg)
-        if getattr(cls, 'condition', None):
-            warn('use "use rql_condition(expression)" instead of using condition',
-                 DeprecationWarning)
-            cls.__select__ &= rql_condition(cls.condition)
-        return cls
-    return plug_selector
-
-@unbind_method
-def has_relation_compat(registered):
-    def plug_selector(cls, vreg):
-        cls = registered(cls, vreg)
-        if getattr(cls, 'etype', None):
-            warn('use relation_possible selector instead of using etype_rtype',
-                 DeprecationWarning)
-            cls.__select__ &= relation_possible(cls.rtype, role(cls),
-                                                getattr(cls, 'etype', None),
-                                                action=getattr(cls, 'require_permission', 'read'))
-        return cls
-    return plug_selector
--- a/server/hooksmanager.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/hooksmanager.py	Fri Aug 14 00:02:08 2009 +0200
@@ -90,7 +90,7 @@
         class.
         """
         if isinstance(function_or_cls, type) and issubclass(function_or_cls, Hook):
-            for event, ertype in function_or_cls.register_to():
+            for event, ertype in function_or_cls.register_to(self.schema):
                 for hook in self._hooks[event][ertype]:
                     if getattr(hook, 'im_self', None).__class__ is function_or_cls:
                         self._hooks[event][ertype].remove(hook)
@@ -213,16 +213,16 @@
     enabled = True
 
     def __init__(self, event=None):
-        super(Hook, self).__init__()
+        super(Hook, self).__init__(None)
         self.event = event
 
     @classmethod
-    def registered(cls, vreg):
-        super(Hook, cls).registered(vreg)
+    def __registered__(cls, vreg):
+        super(Hook, cls).__registered__(vreg)
         return cls()
 
     @classmethod
-    def register_to(cls):
+    def register_to(cls, schema):
         if not cls.enabled:
             cls.warning('%s hook has been disabled', cls)
             return
@@ -241,7 +241,7 @@
                 yield event, ertype
                 done.add((event, ertype))
                 try:
-                    eschema = cls.schema.eschema(ertype)
+                    eschema = schema.eschema(ertype)
                 except KeyError:
                     # relation schema
                     pass
--- a/server/migractions.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/migractions.py	Fri Aug 14 00:02:08 2009 +0200
@@ -254,9 +254,9 @@
                         'fsschema': self.fs_schema,
                         'session' : self.session,
                         'repo' : self.repo,
-                        'synchronize_schema': deprecated()(self.cmd_sync_schema_props_perms),
-                        'synchronize_eschema': deprecated()(self.cmd_sync_schema_props_perms),
-                        'synchronize_rschema': deprecated()(self.cmd_sync_schema_props_perms),
+                        'synchronize_schema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
+                        'synchronize_eschema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
+                        'synchronize_rschema': deprecated()(self.cmd_sync_schema_props_perms), # 3.4
                         })
         return context
 
@@ -909,7 +909,7 @@
         if commit:
             self.commit()
 
-    @deprecated('use sync_schema_props_perms(ertype, syncprops=False)')
+    @deprecated('[3.4] use sync_schema_props_perms(ertype, syncprops=False)')
     def cmd_synchronize_permissions(self, ertype, commit=True):
         self.cmd_sync_schema_props_perms(ertype, syncprops=False, commit=commit)
 
--- a/server/repository.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/repository.py	Fri Aug 14 00:02:08 2009 +0200
@@ -574,7 +574,7 @@
         finally:
             session.close()
         session = Session(user, self, cnxprops)
-        user.req = user.rset.req = session
+        user.cw_req = user.rset.req = session
         user.clear_related_cache()
         self._sessions[session.id] = session
         self.info('opened %s', session)
--- a/server/serverconfig.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/serverconfig.py	Fri Aug 14 00:02:08 2009 +0200
@@ -263,7 +263,9 @@
         except RegistryNotFound:
             return hooks
         for hookdef in apphookdefs:
-            for event, ertype in hookdef.register_to():
+            # XXX < 3.5 bw compat
+            hookdef.__dict__['config'] = self
+            for event, ertype in hookdef.register_to(vreg.schema):
                 if ertype == 'Any':
                     ertype = ''
                 cb = hookdef.make_callback(event)
--- a/server/serverctl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/serverctl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -14,8 +14,8 @@
 from logilab.common.clcommands import register_commands, cmd_run, pop_arg
 from logilab.common.shellutils import ASK
 
-from cubicweb import AuthenticationError, ExecutionError, ConfigurationError, underline_title
-from cubicweb.toolsutils import Command, CommandHandler
+from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
+from cubicweb.toolsutils import Command, CommandHandler, underline_title
 from cubicweb.server import SOURCE_TYPES
 from cubicweb.server.utils import ask_source_config
 from cubicweb.server.serverconfig import USER_OPTIONS, ServerConfiguration
--- a/server/session.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/session.py	Fri Aug 14 00:02:08 2009 +0200
@@ -15,7 +15,8 @@
 from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
 from yams import BASE_TYPES
 
-from cubicweb import RequestSessionMixIn, Binary, UnknownEid
+from cubicweb import Binary, UnknownEid
+from cubicweb.req import RequestSessionBase
 from cubicweb.dbapi import ConnectionProperties
 from cubicweb.utils import make_uid
 from cubicweb.server.rqlrewrite import RQLRewriter
@@ -41,7 +42,7 @@
     return description
 
 
-class Session(RequestSessionMixIn):
+class Session(RequestSessionBase):
     """tie session id, user, connections pool and other session data all
     together
     """
@@ -552,12 +553,12 @@
             description.append(tuple(row_descr))
         return description
 
-    @deprecated("use vreg['etypes'].etype_class(etype)")
+    @deprecated("[3.4] use vreg['etypes'].etype_class(etype)")
     def etype_class(self, etype):
         """return an entity class for the given entity type"""
         return self.vreg['etypes'].etype_class(etype)
 
-    @deprecated('use direct access to session.transaction_data')
+    @deprecated('[3.4] use direct access to session.transaction_data')
     def query_data(self, key, default=None, setdefault=False, pop=False):
         if setdefault:
             assert not pop
@@ -567,7 +568,7 @@
         else:
             return self.transaction_data.get(key, default)
 
-    @deprecated('use entity_from_eid(eid, etype=None)')
+    @deprecated('[3.4] use entity_from_eid(eid, etype=None)')
     def entity(self, eid):
         """return a result set for the given eid"""
         return self.entity_from_eid(eid)
--- a/server/test/unittest_checkintegrity.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_checkintegrity.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,7 +13,7 @@
 
 from cubicweb.server.checkintegrity import check
 
-repo, cnx = init_test_database('sqlite')
+repo, cnx = init_test_database()
 
 class CheckIntegrityTC(TestCase):
     def test(self):
--- a/server/test/unittest_hookhelper.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_hookhelper.py	Fri Aug 14 00:02:08 2009 +0200
@@ -8,18 +8,28 @@
 """
 
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.server.pool import LateOperation, Operation, SingleLastOperation
 from cubicweb.server.hookhelper import *
+from cubicweb.server import hooks, schemahooks
 
 
-class HookHelpersTC(RepositoryBasedTC):
+def clean_session_ops(func):
+    def wrapper(self, *args, **kwargs):
+        try:
+            return func(self, *args, **kwargs)
+        finally:
+            self.session.pending_operations[:] = []
+    return wrapper
+
+class HookHelpersTC(CubicWebTC):
 
     def setUp(self):
-        RepositoryBasedTC.setUp(self)
+        CubicWebTC.setUp(self)
         self.hm = self.repo.hm
 
+    @clean_session_ops
     def test_late_operation(self):
         session = self.session
         l1 = LateOperation(session)
@@ -27,6 +37,7 @@
         l3 = Operation(session)
         self.assertEquals(session.pending_operations, [l3, l1, l2])
 
+    @clean_session_ops
     def test_single_last_operation(self):
         session = self.session
         l0 = SingleLastOperation(session)
@@ -37,8 +48,8 @@
         l4 = SingleLastOperation(session)
         self.assertEquals(session.pending_operations, [l3, l1, l2, l4])
 
+    @clean_session_ops
     def test_global_operation_order(self):
-        from cubicweb.server import hooks, schemahooks
         session = self.session
         op1 = hooks.DelayedDeleteOp(session)
         op2 = schemahooks.MemSchemaRDefDel(session)
@@ -80,10 +91,7 @@
         self.assertEquals(len(searchedops), 1,
                           self.session.pending_operations)
         self.commit()
-        searchedops = [op for op in self.session.pending_operations
-                       if isinstance(op, SendMailOp)]
-        self.assertEquals(len(searchedops), 0,
-                          self.session.pending_operations)
+        self.assertEquals([], self.session.pending_operations)
 
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_hooks.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_hooks.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,7 +11,7 @@
 
 from cubicweb import (ConnectionError, RepositoryError, ValidationError,
                       AuthenticationError, BadConnectionId)
-from cubicweb.devtools.apptest import RepositoryBasedTC, get_versions
+from cubicweb.devtools.testlib import CubicWebTC, get_versions
 
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.repository import Repository
@@ -26,7 +26,7 @@
 
 
 
-class CoreHooksTC(RepositoryBasedTC):
+class CoreHooksTC(CubicWebTC):
 
     def test_delete_internal_entities(self):
         self.assertRaises(RepositoryError, self.execute,
@@ -62,7 +62,7 @@
 
     def test_delete_if_singlecard1(self):
         self.assertEquals(self.repo.schema['in_state'].inlined, False)
-        ueid = self.create_user('toto')
+        ueid = self.create_user('toto').eid
         self.commit()
         self.execute('SET X in_state S WHERE S name "deactivated", X eid %(x)s', {'x': ueid})
         rset = self.execute('Any S WHERE X in_state S, X eid %(x)s', {'x': ueid})
@@ -156,7 +156,7 @@
 
 
 
-class UserGroupHooksTC(RepositoryBasedTC):
+class UserGroupHooksTC(CubicWebTC):
 
     def test_user_synchronization(self):
         self.create_user('toto', password='hop', commit=False)
@@ -164,7 +164,7 @@
                           self.repo.connect, u'toto', 'hop')
         self.commit()
         cnxid = self.repo.connect(u'toto', 'hop')
-        self.failIfEqual(cnxid, self.cnxid)
+        self.failIfEqual(cnxid, self.session.id)
         self.execute('DELETE CWUser X WHERE X login "toto"')
         self.repo.execute(cnxid, 'State X')
         self.commit()
@@ -184,7 +184,7 @@
         self.assertEquals(user.groups, set(('managers',)))
 
     def test_user_composite_owner(self):
-        ueid = self.create_user('toto')
+        ueid = self.create_user('toto').eid
         # composite of euser should be owned by the euser regardless of who created it
         self.execute('INSERT EmailAddress X: X address "toto@logilab.fr", U use_email X '
                      'WHERE U login "toto"')
@@ -200,7 +200,7 @@
         self.failIf(self.execute('Any X WHERE X created_by Y, X eid >= %(x)s', {'x': eid}))
 
 
-class CWPropertyHooksTC(RepositoryBasedTC):
+class CWPropertyHooksTC(CubicWebTC):
 
     def test_unexistant_eproperty(self):
         ex = self.assertRaises(ValidationError,
@@ -224,7 +224,7 @@
         self.assertEquals(ex.errors, {'value': u'unauthorized value'})
 
 
-class SchemaHooksTC(RepositoryBasedTC):
+class SchemaHooksTC(CubicWebTC):
 
     def test_duplicate_etype_error(self):
         # check we can't add a CWEType or CWRType entity if it already exists one
@@ -246,24 +246,23 @@
             self.assertEquals(ex.errors, {'login': 'the value "admin" is already used, use another one'})
 
 
-class SchemaModificationHooksTC(RepositoryBasedTC):
+class SchemaModificationHooksTC(CubicWebTC):
 
-    def setUp(self):
-        if not hasattr(self, '_repo'):
-            # first initialization
-            repo = self.repo # set by the RepositoryBasedTC metaclass
-            # force to read schema from the database to get proper eid set on schema instances
-            repo.config._cubes = None
-            repo.fill_schema()
-        RepositoryBasedTC.setUp(self)
+    @classmethod
+    def init_config(cls, config):
+        super(SchemaModificationHooksTC, cls).init_config(config)
+        config._cubes = None
+        cls.repo.fill_schema()
 
     def index_exists(self, etype, attr, unique=False):
+        self.session.set_pool()
         dbhelper = self.session.pool.source('system').dbhelper
         sqlcursor = self.session.pool['system']
         return dbhelper.index_exists(sqlcursor, SQL_PREFIX + etype, SQL_PREFIX + attr, unique=unique)
 
     def test_base(self):
         schema = self.repo.schema
+        self.session.set_pool()
         dbhelper = self.session.pool.source('system').dbhelper
         sqlcursor = self.session.pool['system']
         self.failIf(schema.has_entity('Societe2'))
@@ -381,6 +380,7 @@
     # schema modification hooks tests #########################################
 
     def test_uninline_relation(self):
+        self.session.set_pool()
         dbhelper = self.session.pool.source('system').dbhelper
         sqlcursor = self.session.pool['system']
         # Personne inline2 Affaire inline
@@ -415,6 +415,7 @@
             self.assertEquals(rset.rows[0], [peid, aeid])
 
     def test_indexed_change(self):
+        self.session.set_pool()
         dbhelper = self.session.pool.source('system').dbhelper
         sqlcursor = self.session.pool['system']
         try:
@@ -433,6 +434,7 @@
             self.failIf(self.index_exists('Affaire', 'sujet'))
 
     def test_unique_change(self):
+        self.session.set_pool()
         dbhelper = self.session.pool.source('system').dbhelper
         sqlcursor = self.session.pool['system']
         try:
@@ -481,10 +483,9 @@
         self.commit()
 
 
-class WorkflowHooksTC(RepositoryBasedTC):
+class WorkflowHooksTC(CubicWebTC):
 
-    def setUp(self):
-        RepositoryBasedTC.setUp(self)
+    def setup_database(self):
         self.s_activated = self.execute('State X WHERE X name "activated"')[0][0]
         self.s_deactivated = self.execute('State X WHERE X name "deactivated"')[0][0]
         self.s_dummy = self.execute('INSERT State X: X name "dummy", X state_of E WHERE E name "CWUser"')[0][0]
@@ -493,12 +494,6 @@
         # so we can test wf enforcing on euser (managers don't have anymore this
         # enforcement
         self.execute('SET X require_group G WHERE G name "users", X transition_of ET, ET name "CWUser"')
-        self.commit()
-
-    def tearDown(self):
-        self.execute('DELETE X require_group G WHERE G name "users", X transition_of ET, ET name "CWUser"')
-        self.commit()
-        RepositoryBasedTC.tearDown(self)
 
     def test_set_initial_state(self):
         ueid = self.execute('INSERT CWUser E: E login "x", E upassword "x", E in_group G '
@@ -511,13 +506,12 @@
         self.assertEquals(initialstate, u'activated')
 
     def test_initial_state(self):
-        cnx = self.login('stduser')
-        cu = cnx.cursor()
-        self.assertRaises(ValidationError, cu.execute,
+        self.login('stduser')
+        self.assertRaises(ValidationError, self.execute,
                           'INSERT CWUser X: X login "badaboum", X upassword %(pwd)s, '
                           'X in_state S WHERE S name "deactivated"', {'pwd': 'oops'})
-        cnx.close()
         # though managers can do whatever he want
+        self.restore_connection()
         self.execute('INSERT CWUser X: X login "badaboum", X upassword %(pwd)s, '
                      'X in_state S, X in_group G WHERE S name "deactivated", G name "users"', {'pwd': 'oops'})
         self.commit()
@@ -526,7 +520,7 @@
     def test_transition_checking1(self):
         cnx = self.login('stduser')
         cu = cnx.cursor()
-        ueid = cnx.user(self.current_session()).eid
+        ueid = cnx.user(self.session).eid
         self.assertRaises(ValidationError,
                           cu.execute, 'SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
                           {'x': ueid, 's': self.s_activated}, 'x')
@@ -535,7 +529,7 @@
     def test_transition_checking2(self):
         cnx = self.login('stduser')
         cu = cnx.cursor()
-        ueid = cnx.user(self.current_session()).eid
+        ueid = cnx.user(self.session).eid
         self.assertRaises(ValidationError,
                           cu.execute, 'SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
                           {'x': ueid, 's': self.s_dummy}, 'x')
@@ -544,7 +538,7 @@
     def test_transition_checking3(self):
         cnx = self.login('stduser')
         cu = cnx.cursor()
-        ueid = cnx.user(self.current_session()).eid
+        ueid = cnx.user(self.session).eid
         cu.execute('SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
                       {'x': ueid, 's': self.s_deactivated}, 'x')
         cnx.commit()
@@ -560,7 +554,7 @@
     def test_transition_checking4(self):
         cnx = self.login('stduser')
         cu = cnx.cursor()
-        ueid = cnx.user(self.current_session()).eid
+        ueid = cnx.user(self.session).eid
         cu.execute('SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
                    {'x': ueid, 's': self.s_deactivated}, 'x')
         cnx.commit()
@@ -602,7 +596,7 @@
         self.assertEquals(tr.owned_by[0].login, 'admin')
 
     def test_transition_information_on_creation(self):
-        ueid = self.create_user('toto')
+        ueid = self.create_user('toto').eid
         rset = self.execute('TrInfo T WHERE T wf_info_for X, X eid %(x)s', {'x': ueid})
         self.assertEquals(len(rset), 1)
         tr = rset.get_entity(0, 0)
--- a/server/test/unittest_hooksmanager.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_hooksmanager.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,7 +6,7 @@
 
 from cubicweb.server.hooksmanager import HooksManager, Hook
 from cubicweb.devtools import TestServerConfiguration
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 class HookCalled(Exception): pass
 
@@ -144,7 +144,7 @@
         self.called.append((subject, r_type, object))
 
 
-class SystemHooksTC(RepositoryBasedTC):
+class SystemHooksTC(CubicWebTC):
 
     def test_startup_shutdown(self):
         import hooks # cubicweb/server/test/data/hooks.py
@@ -168,9 +168,9 @@
     events = ('whatever', 'another')
     accepts = ('Societe', 'Division')
 
-class HookTC(RepositoryBasedTC):
+class HookTC(CubicWebTC):
     def test_inheritance(self):
-        self.assertEquals(list(MyHook.register_to()),
+        self.assertEquals(list(MyHook.register_to(self.schema)),
                           zip(repeat('whatever'), ('Societe', 'Division', 'SubDivision'))
                           + zip(repeat('another'), ('Societe', 'Division', 'SubDivision')))
 
--- a/server/test/unittest_ldapuser.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_ldapuser.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,8 +7,8 @@
 """
 
 from logilab.common.testlib import TestCase, unittest_main, mock_object
-from cubicweb.devtools import init_test_database, TestServerConfiguration
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools import TestServerConfiguration
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.repotest import RQLGeneratorTC
 
 from cubicweb.server.sources.ldapuser import *
@@ -34,36 +34,31 @@
 
 
 
-config = TestServerConfiguration('data')
-config.sources_file = lambda : 'data/sourcesldap'
-repo, cnx = init_test_database('sqlite', config=config)
-
-class LDAPUserSourceTC(RepositoryBasedTC):
-    repo, cnx = repo, cnx
+class LDAPUserSourceTC(CubicWebTC):
+    config = TestServerConfiguration('data')
+    config.sources_file = lambda : 'data/sourcesldap'
 
     def patch_authenticate(self):
         self._orig_authenticate = LDAPUserSource.authenticate
         LDAPUserSource.authenticate = nopwd_authenticate
 
-    def setUp(self):
-        self._prepare()
+    def setup_database(self):
         # XXX: need this first query else we get 'database is locked' from
         # sqlite since it doesn't support multiple connections on the same
         # database
         # so doing, ldap inserted users don't get removed between each test
-        rset = self.execute('CWUser X')
-        self.commit()
+        rset = self.sexecute('CWUser X')
         # check we get some users from ldap
         self.assert_(len(rset) > 1)
-        self.maxeid = self.execute('Any MAX(X)')[0][0]
 
     def tearDown(self):
         if hasattr(self, '_orig_authenticate'):
             LDAPUserSource.authenticate = self._orig_authenticate
-        RepositoryBasedTC.tearDown(self)
+        CubicWebTC.tearDown(self)
 
     def test_authenticate(self):
         source = self.repo.sources_by_uri['ldapuser']
+        self.session.set_pool()
         self.assertRaises(AuthenticationError,
                           source.authenticate, self.session, 'toto', 'toto')
 
@@ -73,7 +68,7 @@
 
     def test_base(self):
         # check a known one
-        e = self.execute('CWUser X WHERE X login "syt"').get_entity(0, 0)
+        e = self.sexecute('CWUser X WHERE X login "syt"').get_entity(0, 0)
         self.assertEquals(e.login, 'syt')
         e.complete()
         self.assertEquals(e.creation_date, None)
@@ -85,73 +80,73 @@
         self.assertEquals(e.created_by, [])
         self.assertEquals(e.primary_email[0].address, 'Sylvain Thenault')
         # email content should be indexed on the user
-        rset = self.execute('CWUser X WHERE X has_text "thenault"')
+        rset = self.sexecute('CWUser X WHERE X has_text "thenault"')
         self.assertEquals(rset.rows, [[e.eid]])
 
     def test_not(self):
-        eid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('CWUser X WHERE NOT X eid %s' % eid)
+        eid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        rset = self.sexecute('CWUser X WHERE NOT X eid %s' % eid)
         self.assert_(rset)
         self.assert_(not eid in (r[0] for r in rset))
 
     def test_multiple(self):
-        seid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        aeid = self.execute('CWUser X WHERE X login "adim"')[0][0]
-        rset = self.execute('CWUser X, Y WHERE X login "syt", Y login "adim"')
+        seid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        aeid = self.sexecute('CWUser X WHERE X login "adim"')[0][0]
+        rset = self.sexecute('CWUser X, Y WHERE X login "syt", Y login "adim"')
         self.assertEquals(rset.rows, [[seid, aeid]])
-        rset = self.execute('Any X,Y,L WHERE X login L, X login "syt", Y login "adim"')
+        rset = self.sexecute('Any X,Y,L WHERE X login L, X login "syt", Y login "adim"')
         self.assertEquals(rset.rows, [[seid, aeid, 'syt']])
 
     def test_in(self):
-        seid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        aeid = self.execute('CWUser X WHERE X login "adim"')[0][0]
-        rset = self.execute('Any X,L ORDERBY L WHERE X login IN("syt", "adim"), X login L')
+        seid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        aeid = self.sexecute('CWUser X WHERE X login "adim"')[0][0]
+        rset = self.sexecute('Any X,L ORDERBY L WHERE X login IN("syt", "adim"), X login L')
         self.assertEquals(rset.rows, [[aeid, 'adim'], [seid, 'syt']])
 
     def test_relations(self):
-        eid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('Any X,E WHERE X is CWUser, X login L, X primary_email E')
+        eid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        rset = self.sexecute('Any X,E WHERE X is CWUser, X login L, X primary_email E')
         self.assert_(eid in (r[0] for r in rset))
-        rset = self.execute('Any X,L,E WHERE X is CWUser, X login L, X primary_email E')
+        rset = self.sexecute('Any X,L,E WHERE X is CWUser, X login L, X primary_email E')
         self.assert_('syt' in (r[1] for r in rset))
 
     def test_count(self):
-        nbusers = self.execute('Any COUNT(X) WHERE X is CWUser')[0][0]
+        nbusers = self.sexecute('Any COUNT(X) WHERE X is CWUser')[0][0]
         # just check this is a possible number
         self.assert_(nbusers > 1, nbusers)
         self.assert_(nbusers < 30, nbusers)
 
     def test_upper(self):
-        eid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('Any UPPER(L) WHERE X eid %s, X login L' % eid)
+        eid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        rset = self.sexecute('Any UPPER(L) WHERE X eid %s, X login L' % eid)
         self.assertEquals(rset[0][0], 'SYT')
 
     def test_unknown_attr(self):
-        eid = self.execute('CWUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('Any L,C,M WHERE X eid %s, X login L, '
+        eid = self.sexecute('CWUser X WHERE X login "syt"')[0][0]
+        rset = self.sexecute('Any L,C,M WHERE X eid %s, X login L, '
                             'X creation_date C, X modification_date M' % eid)
         self.assertEquals(rset[0][0], 'syt')
         self.assertEquals(rset[0][1], None)
         self.assertEquals(rset[0][2], None)
 
     def test_sort(self):
-        logins = [l for l, in self.execute('Any L ORDERBY L WHERE X login L')]
+        logins = [l for l, in self.sexecute('Any L ORDERBY L WHERE X login L')]
         self.assertEquals(logins, sorted(logins))
 
     def test_lower_sort(self):
-        logins = [l for l, in self.execute('Any L ORDERBY lower(L) WHERE X login L')]
+        logins = [l for l, in self.sexecute('Any L ORDERBY lower(L) WHERE X login L')]
         self.assertEquals(logins, sorted(logins))
 
     def test_or(self):
-        rset = self.execute('DISTINCT Any X WHERE X login "syt" OR (X in_group G, G name "managers")')
+        rset = self.sexecute('DISTINCT Any X WHERE X login "syt" OR (X in_group G, G name "managers")')
         self.assertEquals(len(rset), 2, rset.rows) # syt + admin
 
     def test_nonregr_set_owned_by(self):
         # test that when a user coming from ldap is triggering a transition
         # the related TrInfo has correct owner information
-        self.execute('SET X in_group G WHERE X login "syt", G name "managers"')
+        self.sexecute('SET X in_group G WHERE X login "syt", G name "managers"')
         self.commit()
-        syt = self.execute('CWUser X WHERE X login "syt"').get_entity(0, 0)
+        syt = self.sexecute('CWUser X WHERE X login "syt"').get_entity(0, 0)
         self.assertEquals([g.name for g in syt.in_group], ['managers', 'users'])
         self.patch_authenticate()
         cnx = self.login('syt', 'dummypassword')
@@ -159,12 +154,12 @@
         cu.execute('SET X in_state S WHERE X login "alf", S name "deactivated"')
         try:
             cnx.commit()
-            alf = self.execute('CWUser X WHERE X login "alf"').get_entity(0, 0)
+            alf = self.sexecute('CWUser X WHERE X login "alf"').get_entity(0, 0)
             self.assertEquals(alf.in_state[0].name, 'deactivated')
             trinfo = alf.latest_trinfo()
             self.assertEquals(trinfo.owned_by[0].login, 'syt')
             # select from_state to skip the user's creation TrInfo
-            rset = self.execute('Any U ORDERBY D DESC WHERE WF wf_info_for X,'
+            rset = self.sexecute('Any U ORDERBY D DESC WHERE WF wf_info_for X,'
                                 'WF creation_date D, WF from_state FS,'
                                 'WF owned_by U?, X eid %(x)s',
                                 {'x': alf.eid}, 'x')
@@ -172,76 +167,76 @@
         finally:
             # restore db state
             self.restore_connection()
-            self.execute('SET X in_state S WHERE X login "alf", S name "activated"')
-            self.execute('DELETE X in_group G WHERE X login "syt", G name "managers"')
+            self.sexecute('SET X in_state S WHERE X login "alf", S name "activated"')
+            self.sexecute('DELETE X in_group G WHERE X login "syt", G name "managers"')
 
     def test_same_column_names(self):
-        self.execute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"')
+        self.sexecute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"')
 
     def test_multiple_entities_from_different_sources(self):
-        self.create_user('cochon')
-        self.failUnless(self.execute('Any X,Y WHERE X login "syt", Y login "cochon"'))
+        self.create_user('cochon', req=self.session)
+        self.failUnless(self.sexecute('Any X,Y WHERE X login "syt", Y login "cochon"'))
 
     def test_exists1(self):
-        self.add_entity('CWGroup', name=u'bougloup1')
-        self.add_entity('CWGroup', name=u'bougloup2')
-        self.execute('SET U in_group G WHERE G name ~= "bougloup%", U login "admin"')
-        self.execute('SET U in_group G WHERE G name = "bougloup1", U login "syt"')
-        rset = self.execute('Any L,SN ORDERBY L WHERE X in_state S, S name SN, X login L, EXISTS(X in_group G, G name ~= "bougloup%")')
+        self.add_entity('CWGroup', name=u'bougloup1', req=self.session)
+        self.add_entity('CWGroup', name=u'bougloup2', req=self.session)
+        self.sexecute('SET U in_group G WHERE G name ~= "bougloup%", U login "admin"')
+        self.sexecute('SET U in_group G WHERE G name = "bougloup1", U login "syt"')
+        rset = self.sexecute('Any L,SN ORDERBY L WHERE X in_state S, S name SN, X login L, EXISTS(X in_group G, G name ~= "bougloup%")')
         self.assertEquals(rset.rows, [['admin', 'activated'], ['syt', 'activated']])
 
     def test_exists2(self):
-        self.create_user('comme')
-        self.create_user('cochon')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "cochon"')
-        rset = self.execute('Any GN ORDERBY GN WHERE X in_group G, G name GN, (G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon")))')
+        self.create_user('comme', req=self.session)
+        self.create_user('cochon', req=self.session)
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
+        rset = self.sexecute('Any GN ORDERBY GN WHERE X in_group G, G name GN, (G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon")))')
         self.assertEquals(rset.rows, [['managers'], ['users']])
 
     def test_exists3(self):
-        self.create_user('comme')
-        self.create_user('cochon')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "cochon"')
-        self.failUnless(self.execute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"'))
-        self.execute('SET X copain Y WHERE X login "syt", Y login "cochon"')
-        self.failUnless(self.execute('Any X, Y WHERE X copain Y, X login "syt", Y login "cochon"'))
-        rset = self.execute('Any GN,L WHERE X in_group G, X login L, G name GN, G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon"))')
+        self.create_user('comme', req=self.session)
+        self.create_user('cochon', req=self.session)
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
+        self.failUnless(self.sexecute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"'))
+        self.sexecute('SET X copain Y WHERE X login "syt", Y login "cochon"')
+        self.failUnless(self.sexecute('Any X, Y WHERE X copain Y, X login "syt", Y login "cochon"'))
+        rset = self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon"))')
         self.assertEquals(sorted(rset.rows), [['managers', 'admin'], ['users', 'comme'], ['users', 'syt']])
 
     def test_exists4(self):
-        self.create_user('comme')
-        self.create_user('cochon', groups=('users', 'guests'))
-        self.create_user('billy')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "cochon"')
-        self.execute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "billy"')
-        self.execute('SET X copain Y WHERE X login "syt", Y login "billy"')
+        self.create_user('comme', req=self.session)
+        self.create_user('cochon', groups=('users', 'guests'), req=self.session)
+        self.create_user('billy', req=self.session)
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
+        self.sexecute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "billy"')
+        self.sexecute('SET X copain Y WHERE X login "syt", Y login "billy"')
         # search for group name, login where
         #   CWUser copain with "comme" or "cochon" AND same login as the copain
         # OR
         #   CWUser in_state activated AND not copain with billy
         #
         # SO we expect everybody but "comme" and "syt"
-        rset= self.execute('Any GN,L WHERE X in_group G, X login L, G name GN, '
+        rset= self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, '
                            'EXISTS(X copain T, T login L, T login in ("comme", "cochon")) OR '
                            'EXISTS(X in_state S, S name "activated", NOT X copain T2, T2 login "billy")')
-        all = self.execute('Any GN, L WHERE X in_group G, X login L, G name GN')
+        all = self.sexecute('Any GN, L WHERE X in_group G, X login L, G name GN')
         all.rows.remove(['users', 'comme'])
         all.rows.remove(['users', 'syt'])
         self.assertEquals(sorted(rset.rows), sorted(all.rows))
 
     def test_exists5(self):
-        self.create_user('comme')
-        self.create_user('cochon', groups=('users', 'guests'))
-        self.create_user('billy')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "cochon"')
-        self.execute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
-        self.execute('SET X copain Y WHERE X login "comme", Y login "billy"')
-        self.execute('SET X copain Y WHERE X login "syt", Y login "cochon"')
-        rset= self.execute('Any L WHERE X login L, '
+        self.create_user('comme', req=self.session)
+        self.create_user('cochon', groups=('users', 'guests'), req=self.session)
+        self.create_user('billy', req=self.session)
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "cochon"')
+        self.sexecute('SET X copain Y WHERE X login "cochon", Y login "cochon"')
+        self.sexecute('SET X copain Y WHERE X login "comme", Y login "billy"')
+        self.sexecute('SET X copain Y WHERE X login "syt", Y login "cochon"')
+        rset= self.sexecute('Any L WHERE X login L, '
                            'EXISTS(X copain T, T login in ("comme", "cochon")) AND '
                            'NOT EXISTS(X copain T2, T2 login "billy")')
         self.assertEquals(sorted(rset.rows), [['cochon'], ['syt']])
-        rset= self.execute('Any GN,L WHERE X in_group G, X login L, G name GN, '
+        rset= self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, '
                            'EXISTS(X copain T, T login in ("comme", "cochon")) AND '
                            'NOT EXISTS(X copain T2, T2 login "billy")')
         self.assertEquals(sorted(rset.rows), [['guests', 'cochon'],
@@ -249,18 +244,18 @@
                                               ['users', 'syt']])
 
     def test_cd_restriction(self):
-        rset = self.execute('CWUser X WHERE X creation_date > "2009-02-01"')
+        rset = self.sexecute('CWUser X WHERE X creation_date > "2009-02-01"')
         self.assertEquals(len(rset), 2) # admin/anon but no ldap user since it doesn't support creation_date
 
     def test_union(self):
-        afeids = self.execute('State X')
-        ueids = self.execute('CWUser X')
-        rset = self.execute('(Any X WHERE X is State) UNION (Any X WHERE X is CWUser)')
+        afeids = self.sexecute('State X')
+        ueids = self.sexecute('CWUser X')
+        rset = self.sexecute('(Any X WHERE X is State) UNION (Any X WHERE X is CWUser)')
         self.assertEquals(sorted(r[0] for r in rset.rows),
                           sorted(r[0] for r in afeids + ueids))
 
     def _init_security_test(self):
-        self.create_user('iaminguestsgrouponly', groups=('guests',))
+        self.create_user('iaminguestsgrouponly', groups=('guests',), req=self.session)
         cnx = self.login('iaminguestsgrouponly')
         return cnx.cursor()
 
@@ -286,33 +281,33 @@
         self.assertEquals(rset.rows, [[None]])
 
     def test_nonregr1(self):
-        self.execute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E owned_by X, '
+        self.sexecute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E owned_by X, '
                      'X modification_date AA',
-                     {'x': cnx.user(self.session).eid})
+                     {'x': self.session.user.eid})
 
     def test_nonregr2(self):
-        self.execute('Any X,L,AA WHERE E eid %(x)s, E owned_by X, '
+        self.sexecute('Any X,L,AA WHERE E eid %(x)s, E owned_by X, '
                      'X login L, X modification_date AA',
-                     {'x': cnx.user(self.session).eid})
+                     {'x': self.session.user.eid})
 
     def test_nonregr3(self):
-        self.execute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, '
+        self.sexecute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, '
                      'X modification_date AA',
-                     {'x': cnx.user(self.session).eid})
+                     {'x': self.session.user.eid})
 
     def test_nonregr4(self):
-        emaileid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org"')[0][0]
-        self.execute('Any X,AA WHERE X use_email Y, Y eid %(x)s, X modification_date AA',
+        emaileid = self.sexecute('INSERT EmailAddress X: X address "toto@logilab.org"')[0][0]
+        self.sexecute('Any X,AA WHERE X use_email Y, Y eid %(x)s, X modification_date AA',
                      {'x': emaileid})
 
     def test_nonregr5(self):
         # original jpl query:
         # Any X, NOW - CD, P WHERE P is Project, U interested_in P, U is CWUser, U login "sthenault", X concerns P, X creation_date CD ORDERBY CD DESC LIMIT 5
         rql = 'Any X, NOW - CD, P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, U login "%s", P is X, X creation_date CD' % self.session.user.login
-        self.execute(rql, )#{'x': })
+        self.sexecute(rql, )#{'x': })
 
     def test_nonregr6(self):
-        self.execute('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File '
+        self.sexecute('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File '
                      'WITH U,UL BEING (Any U,UL WHERE ME eid %(x)s, (EXISTS(U identity ME) '
                      'OR (EXISTS(U in_group G, G name IN("managers", "staff")))) '
                      'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is CWUser)',
@@ -353,6 +348,9 @@
         res = trfunc.apply([[1, 2], [2, 4], [3, 6], [1, 5]])
         self.assertEquals(res, [[1, 5], [2, 4], [3, 6]])
 
+# XXX
+LDAPUserSourceTC._init_repo()
+repo = LDAPUserSourceTC.repo
 
 class RQL2LDAPFilterTC(RQLGeneratorTC):
     schema = repo.schema
--- a/server/test/unittest_migractions.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_migractions.py	Fri Aug 14 00:02:08 2009 +0200
@@ -2,13 +2,14 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
+from copy import deepcopy
 from datetime import date
 from os.path import join
 
 from logilab.common.testlib import TestCase, unittest_main
 
 from cubicweb import ConfigurationError
-from cubicweb.devtools.apptest import RepositoryBasedTC, get_versions
+from cubicweb.devtools.testlib import CubicWebTC, get_versions
 from cubicweb.schema import CubicWebSchemaLoader
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.repository import Repository
@@ -23,22 +24,28 @@
     Repository.get_versions = orig_get_versions
 
 
-class MigrationCommandsTC(RepositoryBasedTC):
+class MigrationCommandsTC(CubicWebTC):
+
+    @classmethod
+    def init_config(cls, config):
+        super(MigrationCommandsTC, cls).init_config(config)
+        config._cubes = None
+        cls.repo.fill_schema()
+        cls.origschema = deepcopy(cls.repo.schema)
+        # hack to read the schema from data/migrschema
+        config.appid = join('data', 'migratedapp')
+        global migrschema
+        migrschema = config.load_schema()
+        config.appid = 'data'
+        assert 'Folder' in migrschema
+
+    @classmethod
+    def _refresh_repo(cls):
+        super(MigrationCommandsTC, cls)._refresh_repo()
+        cls.repo.schema = cls.vreg.schema = deepcopy(cls.origschema)
 
     def setUp(self):
-        if not hasattr(self, '_repo'):
-            # first initialization
-            repo = self.repo # set by the RepositoryBasedTC metaclass
-            # force to read schema from the database
-            repo.config._cubes = None
-            repo.fill_schema()
-            # hack to read the schema from data/migrschema
-            self.repo.config.appid = join('data', 'migratedapp')
-            global migrschema
-            migrschema = self.repo.config.load_schema()
-            self.repo.config.appid = 'data'
-            assert 'Folder' in migrschema
-        RepositoryBasedTC.setUp(self)
+        CubicWebTC.setUp(self)
         self.mh = ServerMigrationHelper(self.repo.config, migrschema,
                                         repo=self.repo, cnx=self.cnx,
                                         interactive=False)
@@ -280,7 +287,7 @@
             'Any N ORDERBY O WHERE X is CWAttribute, X relation_type RT, RT name N,'
             'X from_entity FE, FE name "Personne",'
             'X ordernum O')]
-        expected = [u'nom', u'prenom', u'promo', u'ass', u'adel', u'titre',
+        expected = [u'nom', u'prenom', u'sexe', u'promo', u'ass', u'adel', u'titre',
                     u'web', u'tel', u'fax', u'datenaiss', u'test', 'description', u'firstname',
                     u'creation_date', 'cwuri', u'modification_date']
         self.assertEquals(rinorder, expected)
--- a/server/test/unittest_msplanner.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_msplanner.py	Fri Aug 14 00:02:08 2009 +0200
@@ -58,7 +58,7 @@
 
 
 # keep cnx so it's not garbage collected and the associated session is closed
-repo, cnx = init_test_database('sqlite')
+repo, cnx = init_test_database()
 
 class BaseMSPlannerTC(BasePlannerTC):
     """test planner related feature on a 3-sources repository:
--- a/server/test/unittest_multisources.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_multisources.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,7 +11,7 @@
 from logilab.common.decorators import cached
 
 from cubicweb.devtools import TestServerConfiguration, init_test_database
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools.testlib import CubicWebTC, refresh_repo
 from cubicweb.devtools.repotest import do_monkey_patch, undo_monkey_patch
 
 TestServerConfiguration.no_sqlite_wrap = True
@@ -26,16 +26,9 @@
 class ExternalSource2Configuration(TestServerConfiguration):
     sourcefile = 'sources_multi2'
 
-repo2, cnx2 = init_test_database('sqlite', config=ExternalSource1Configuration('data'))
-cu = cnx2.cursor()
-ec1 = cu.execute('INSERT Card X: X title "C3: An external card", X wikiid "aaa"')[0][0]
-cu.execute('INSERT Card X: X title "C4: Ze external card", X wikiid "zzz"')
-aff1 = cu.execute('INSERT Affaire X: X ref "AFFREF", X in_state S WHERE S name "pitetre"')[0][0]
-cnx2.commit()
-
 MTIME = datetime.now() - timedelta(0, 10)
-
-repo3, cnx3 = init_test_database('sqlite', config=ExternalSource2Configuration('data'))
+repo2, cnx2 = init_test_database(config=ExternalSource1Configuration('data'))
+repo3, cnx3 = init_test_database(config=ExternalSource2Configuration('data'))
 
 # XXX, access existing connection, no pyro connection
 from cubicweb.server.sources.pyrorql import PyroRQLSource
@@ -45,38 +38,47 @@
 from cubicweb.dbapi import Connection
 Connection.close = lambda x: None
 
-class TwoSourcesTC(RepositoryBasedTC):
-    repo_config = TwoSourcesConfiguration('data')
+class TwoSourcesTC(CubicWebTC):
+    config = TwoSourcesConfiguration('data')
+
+    @classmethod
+    def _refresh_repo(cls):
+        super(TwoSourcesTC, cls)._refresh_repo()
+        cnx2.rollback()
+        refresh_repo(repo2)
+        cnx3.rollback()
+        refresh_repo(repo3)
 
     def setUp(self):
-        RepositoryBasedTC.setUp(self)
-        self.repo.sources[-1]._query_cache.clear()
-        self.repo.sources[-2]._query_cache.clear()
-        # trigger discovery
-        self.execute('Card X')
-        self.execute('Affaire X')
-        self.execute('State X')
-        self.commit()
-        # don't delete external entities!
-        self.maxeid = self.session.system_sql('SELECT MAX(eid) FROM entities').fetchone()[0]
-        # add some entities
-        self.ic1 = self.execute('INSERT Card X: X title "C1: An internal card", X wikiid "aaai"')[0][0]
-        self.ic2 = self.execute('INSERT Card X: X title "C2: Ze internal card", X wikiid "zzzi"')[0][0]
-        self.commit()
+        CubicWebTC.setUp(self)
         do_monkey_patch()
 
     def tearDown(self):
-        RepositoryBasedTC.tearDown(self)
+        CubicWebTC.tearDown(self)
         undo_monkey_patch()
 
+    def setup_database(self):
+        cu = cnx2.cursor()
+        self.ec1 = cu.execute('INSERT Card X: X title "C3: An external card", X wikiid "aaa"')[0][0]
+        cu.execute('INSERT Card X: X title "C4: Ze external card", X wikiid "zzz"')
+        self.aff1 = cu.execute('INSERT Affaire X: X ref "AFFREF", X in_state S WHERE S name "pitetre"')[0][0]
+        cnx2.commit()
+        # trigger discovery
+        self.sexecute('Card X')
+        self.sexecute('Affaire X')
+        self.sexecute('State X')
+        # add some entities
+        self.ic1 = self.sexecute('INSERT Card X: X title "C1: An internal card", X wikiid "aaai"')[0][0]
+        self.ic2 = self.sexecute('INSERT Card X: X title "C2: Ze internal card", X wikiid "zzzi"')[0][0]
+
     def test_eid_comp(self):
-        rset = self.execute('Card X WHERE X eid > 1')
+        rset = self.sexecute('Card X WHERE X eid > 1')
         self.assertEquals(len(rset), 4)
-        rset = self.execute('Any X,T WHERE X title T, X eid > 1')
+        rset = self.sexecute('Any X,T WHERE X title T, X eid > 1')
         self.assertEquals(len(rset), 4)
 
     def test_metainformation(self):
-        rset = self.execute('Card X ORDERBY T WHERE X title T')
+        rset = self.sexecute('Card X ORDERBY T WHERE X title T')
         # 2 added to the system source, 2 added to the external source
         self.assertEquals(len(rset), 4)
         # since they are orderd by eid, we know the 3 first one is coming from the system source
@@ -89,28 +91,28 @@
         self.assertEquals(metainf['source'], {'adapter': 'pyrorql', 'base-url': 'http://extern.org/', 'uri': 'extern'})
         self.assertEquals(metainf['type'], 'Card')
         self.assert_(metainf['extid'])
-        etype = self.execute('Any ETN WHERE X is ET, ET name ETN, X eid %(x)s',
+        etype = self.sexecute('Any ETN WHERE X is ET, ET name ETN, X eid %(x)s',
                              {'x': externent.eid}, 'x')[0][0]
         self.assertEquals(etype, 'Card')
 
     def test_order_limit_offset(self):
-        rsetbase = self.execute('Any W,X ORDERBY W,X WHERE X wikiid W')
+        rsetbase = self.sexecute('Any W,X ORDERBY W,X WHERE X wikiid W')
         self.assertEquals(len(rsetbase), 4)
         self.assertEquals(sorted(rsetbase.rows), rsetbase.rows)
-        rset = self.execute('Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WHERE X wikiid W')
+        rset = self.sexecute('Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WHERE X wikiid W')
         self.assertEquals(rset.rows, rsetbase.rows[2:4])
 
     def test_has_text(self):
         self.repo.sources_by_uri['extern'].synchronize(MTIME) # in case fti_update has been run before
-        self.failUnless(self.execute('Any X WHERE X has_text "affref"'))
-        self.failUnless(self.execute('Affaire X WHERE X has_text "affref"'))
+        self.failUnless(self.sexecute('Any X WHERE X has_text "affref"'))
+        self.failUnless(self.sexecute('Affaire X WHERE X has_text "affref"'))
 
     def test_anon_has_text(self):
         self.repo.sources_by_uri['extern'].synchronize(MTIME) # in case fti_update has been run before
-        self.execute('INSERT Affaire X: X ref "no readable card"')[0][0]
-        aff1 = self.execute('INSERT Affaire X: X ref "card"')[0][0]
+        self.sexecute('INSERT Affaire X: X ref "no readable card"')[0][0]
+        aff1 = self.sexecute('INSERT Affaire X: X ref "card"')[0][0]
         # grant read access
-        self.execute('SET X owned_by U WHERE X eid %(x)s, U login "anon"', {'x': aff1}, 'x')
+        self.sexecute('SET X owned_by U WHERE X eid %(x)s, U login "anon"', {'x': aff1}, 'x')
         self.commit()
         cnx = self.login('anon')
         cu = cnx.cursor()
@@ -120,79 +122,81 @@
 
     def test_synchronization(self):
         cu = cnx2.cursor()
-        assert cu.execute('Any X WHERE X eid %(x)s', {'x': aff1}, 'x')
-        cu.execute('SET X ref "BLAH" WHERE X eid %(x)s', {'x': aff1}, 'x')
+        assert cu.execute('Any X WHERE X eid %(x)s', {'x': self.aff1}, 'x')
+        cu.execute('SET X ref "BLAH" WHERE X eid %(x)s', {'x': self.aff1}, 'x')
         aff2 = cu.execute('INSERT Affaire X: X ref "AFFREUX", X in_state S WHERE S name "pitetre"')[0][0]
         cnx2.commit()
         try:
             # force sync
             self.repo.sources_by_uri['extern'].synchronize(MTIME)
-            self.failUnless(self.execute('Any X WHERE X has_text "blah"'))
-            self.failUnless(self.execute('Any X WHERE X has_text "affreux"'))
+            self.failUnless(self.sexecute('Any X WHERE X has_text "blah"'))
+            self.failUnless(self.sexecute('Any X WHERE X has_text "affreux"'))
             cu.execute('DELETE Affaire X WHERE X eid %(x)s', {'x': aff2})
             cnx2.commit()
             self.repo.sources_by_uri['extern'].synchronize(MTIME)
-            rset = self.execute('Any X WHERE X has_text "affreux"')
+            rset = self.sexecute('Any X WHERE X has_text "affreux"')
             self.failIf(rset)
         finally:
             # restore state
-            cu.execute('SET X ref "AFFREF" WHERE X eid %(x)s', {'x': aff1}, 'x')
+            cu.execute('SET X ref "AFFREF" WHERE X eid %(x)s', {'x': self.aff1}, 'x')
             cnx2.commit()
 
     def test_simplifiable_var(self):
-        affeid = self.execute('Affaire X WHERE X ref "AFFREF"')[0][0]
-        rset = self.execute('Any X,AA,AB WHERE E eid %(x)s, E in_state X, X name AA, X modification_date AB',
+        affeid = self.sexecute('Affaire X WHERE X ref "AFFREF"')[0][0]
+        rset = self.sexecute('Any X,AA,AB WHERE E eid %(x)s, E in_state X, X name AA, X modification_date AB',
                             {'x': affeid}, 'x')
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset[0][1], "pitetre")
 
     def test_simplifiable_var_2(self):
-        affeid = self.execute('Affaire X WHERE X ref "AFFREF"')[0][0]
-        rset = self.execute('Any E WHERE E eid %(x)s, E in_state S, NOT S name "moved"',
+        affeid = self.sexecute('Affaire X WHERE X ref "AFFREF"')[0][0]
+        rset = self.sexecute('Any E WHERE E eid %(x)s, E in_state S, NOT S name "moved"',
                             {'x': affeid, 'u': self.session.user.eid}, 'x')
         self.assertEquals(len(rset), 1)
 
     def test_sort_func(self):
-        self.execute('Affaire X ORDERBY DUMB_SORT(RF) WHERE X ref RF')
+        self.sexecute('Affaire X ORDERBY DUMB_SORT(RF) WHERE X ref RF')
 
     def test_sort_func_ambigous(self):
-        self.execute('Any X ORDERBY DUMB_SORT(RF) WHERE X title RF')
+        self.sexecute('Any X ORDERBY DUMB_SORT(RF) WHERE X title RF')
 
     def test_in_eid(self):
-        iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], str(ec1),
+        iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], str(self.ec1),
                                    'Card', self.session)
-        rset = self.execute('Any X WHERE X eid IN (%s, %s)' % (iec1, self.ic1))
+        rset = self.sexecute('Any X WHERE X eid IN (%s, %s)' % (iec1, self.ic1))
         self.assertEquals(sorted(r[0] for r in rset.rows), sorted([iec1, self.ic1]))
 
     def test_greater_eid(self):
-        rset = self.execute('Any X WHERE X eid > %s' % self.maxeid)
+        rset = self.sexecute('Any X WHERE X eid > %s' % (self.ic1 - 1))
         self.assertEquals(len(rset.rows), 2) # self.ic1 and self.ic2
+        cu = cnx2.cursor()
         ec2 = cu.execute('INSERT Card X: X title "glup"')[0][0]
         cnx2.commit()
         # 'X eid > something' should not trigger discovery
-        rset = self.execute('Any X WHERE X eid > %s' % self.maxeid)
+        rset = self.sexecute('Any X WHERE X eid > %s' % (self.ic1 - 1))
         self.assertEquals(len(rset.rows), 2)
         # trigger discovery using another query
-        crset = self.execute('Card X WHERE X title "glup"')
+        crset = self.sexecute('Card X WHERE X title "glup"')
         self.assertEquals(len(crset.rows), 1)
-        rset = self.execute('Any X WHERE X eid > %s' % self.maxeid)
+        rset = self.sexecute('Any X WHERE X eid > %s' % (self.ic1 - 1))
         self.assertEquals(len(rset.rows), 3)
-        rset = self.execute('Any MAX(X)')
+        rset = self.sexecute('Any MAX(X)')
         self.assertEquals(len(rset.rows), 1)
         self.assertEquals(rset.rows[0][0], crset[0][0])
 
     def test_attr_unification_1(self):
-        n1 = self.execute('INSERT Note X: X type "AFFREF"')[0][0]
-        n2 = self.execute('INSERT Note X: X type "AFFREU"')[0][0]
-        rset = self.execute('Any X,Y WHERE X is Note, Y is Affaire, X type T, Y ref T')
+        n1 = self.sexecute('INSERT Note X: X type "AFFREF"')[0][0]
+        n2 = self.sexecute('INSERT Note X: X type "AFFREU"')[0][0]
+        rset = self.sexecute('Any X,Y WHERE X is Note, Y is Affaire, X type T, Y ref T')
         self.assertEquals(len(rset), 1, rset.rows)
 
     def test_attr_unification_2(self):
+        cu = cnx2.cursor()
         ec2 = cu.execute('INSERT Card X: X title "AFFREF"')[0][0]
         cnx2.commit()
         try:
-            c1 = self.execute('INSERT Card C: C title "AFFREF"')[0][0]
-            rset = self.execute('Any X,Y WHERE X is Card, Y is Affaire, X title T, Y ref T')
+            c1 = self.sexecute('INSERT Card C: C title "AFFREF"')[0][0]
+            rset = self.sexecute('Any X,Y WHERE X is Card, Y is Affaire, X title T, Y ref T')
             self.assertEquals(len(rset), 2, rset.rows)
         finally:
             cu.execute('DELETE Card X WHERE X eid %(x)s', {'x': ec2}, 'x')
@@ -200,81 +204,86 @@
 
     def test_attr_unification_neq_1(self):
         # XXX complete
-        self.execute('Any X,Y WHERE X is Note, Y is Affaire, X creation_date D, Y creation_date > D')
+        self.sexecute('Any X,Y WHERE X is Note, Y is Affaire, X creation_date D, Y creation_date > D')
 
     def test_attr_unification_neq_2(self):
         # XXX complete
-        self.execute('Any X,Y WHERE X is Card, Y is Affaire, X creation_date D, Y creation_date > D')
+        self.sexecute('Any X,Y WHERE X is Card, Y is Affaire, X creation_date D, Y creation_date > D')
 
     def test_union(self):
-        afeids = self.execute('Affaire X')
-        ueids = self.execute('CWUser X')
-        rset = self.execute('(Any X WHERE X is Affaire) UNION (Any X WHERE X is CWUser)')
+        afeids = self.sexecute('Affaire X')
+        ueids = self.sexecute('CWUser X')
+        rset = self.sexecute('(Any X WHERE X is Affaire) UNION (Any X WHERE X is CWUser)')
         self.assertEquals(sorted(r[0] for r in rset.rows),
                           sorted(r[0] for r in afeids + ueids))
 
     def test_subquery1(self):
-        rsetbase = self.execute('Any W,X WITH W,X BEING (Any W,X ORDERBY W,X WHERE X wikiid W)')
+        rsetbase = self.sexecute('Any W,X WITH W,X BEING (Any W,X ORDERBY W,X WHERE X wikiid W)')
         self.assertEquals(len(rsetbase), 4)
         self.assertEquals(sorted(rsetbase.rows), rsetbase.rows)
-        rset = self.execute('Any W,X LIMIT 2 OFFSET 2 WITH W,X BEING (Any W,X ORDERBY W,X WHERE X wikiid W)')
+        rset = self.sexecute('Any W,X LIMIT 2 OFFSET 2 WITH W,X BEING (Any W,X ORDERBY W,X WHERE X wikiid W)')
         self.assertEquals(rset.rows, rsetbase.rows[2:4])
-        rset = self.execute('Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WITH W,X BEING (Any W,X WHERE X wikiid W)')
+        rset = self.sexecute('Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WITH W,X BEING (Any W,X WHERE X wikiid W)')
         self.assertEquals(rset.rows, rsetbase.rows[2:4])
-        rset = self.execute('Any W,X WITH W,X BEING (Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WHERE X wikiid W)')
+        rset = self.sexecute('Any W,X WITH W,X BEING (Any W,X ORDERBY W,X LIMIT 2 OFFSET 2 WHERE X wikiid W)')
         self.assertEquals(rset.rows, rsetbase.rows[2:4])
 
     def test_subquery2(self):
-        affeid = self.execute('Affaire X WHERE X ref "AFFREF"')[0][0]
-        rset = self.execute('Any X,AA,AB WITH X,AA,AB BEING (Any X,AA,AB WHERE E eid %(x)s, E in_state X, X name AA, X modification_date AB)',
+        affeid = self.sexecute('Affaire X WHERE X ref "AFFREF"')[0][0]
+        rset = self.sexecute('Any X,AA,AB WITH X,AA,AB BEING (Any X,AA,AB WHERE E eid %(x)s, E in_state X, X name AA, X modification_date AB)',
                             {'x': affeid})
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset[0][1], "pitetre")
 
     def test_not_relation(self):
-        states = set(tuple(x) for x in self.execute('Any S,SN WHERE S is State, S name SN'))
+        states = set(tuple(x) for x in self.sexecute('Any S,SN WHERE S is State, S name SN'))
         userstate = self.session.user.in_state[0]
         states.remove((userstate.eid, userstate.name))
-        notstates = set(tuple(x) for x in self.execute('Any S,SN WHERE S is State, S name SN, NOT X in_state S, X eid %(x)s',
+        notstates = set(tuple(x) for x in self.sexecute('Any S,SN WHERE S is State, S name SN, NOT X in_state S, X eid %(x)s',
                                                        {'x': self.session.user.eid}, 'x'))
         self.assertEquals(notstates, states)
-        aff1 = self.execute('Any X WHERE X is Affaire, X ref "AFFREF"')[0][0]
-        aff1stateeid, aff1statename = self.execute('Any S,SN WHERE X eid %(x)s, X in_state S, S name SN', {'x': aff1}, 'x')[0]
+        aff1 = self.sexecute('Any X WHERE X is Affaire, X ref "AFFREF"')[0][0]
+        aff1stateeid, aff1statename = self.sexecute('Any S,SN WHERE X eid %(x)s, X in_state S, S name SN', {'x': aff1}, 'x')[0]
         self.assertEquals(aff1statename, 'pitetre')
         states.add((userstate.eid, userstate.name))
         states.remove((aff1stateeid, aff1statename))
-        notstates = set(tuple(x) for x in self.execute('Any S,SN WHERE S is State, S name SN, NOT X in_state S, X eid %(x)s',
+        notstates = set(tuple(x) for x in self.sexecute('Any S,SN WHERE S is State, S name SN, NOT X in_state S, X eid %(x)s',
                                                        {'x': aff1}, 'x'))
         self.assertSetEquals(notstates, states)
 
     def test_absolute_url_base_url(self):
+        cu = cnx2.cursor()
         ceid = cu.execute('INSERT Card X: X title "without wikiid to get eid based url"')[0][0]
         cnx2.commit()
-        lc = self.execute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
+        lc = self.sexecute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
         self.assertEquals(lc.absolute_url(), 'http://extern.org/card/eid/%s' % ceid)
+        cu.execute('DELETE Card X WHERE X eid %(x)s', {'x':ceid})
+        cnx2.commit()
 
     def test_absolute_url_no_base_url(self):
         cu = cnx3.cursor()
         ceid = cu.execute('INSERT Card X: X title "without wikiid to get eid based url"')[0][0]
         cnx3.commit()
-        lc = self.execute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
+        lc = self.sexecute('Card X WHERE X title "without wikiid to get eid based url"').get_entity(0, 0)
         self.assertEquals(lc.absolute_url(), 'http://testing.fr/cubicweb/card/eid/%s' % lc.eid)
+        cu.execute('DELETE Card X WHERE X eid %(x)s', {'x':ceid})
+        cnx3.commit()
 
     def test_nonregr1(self):
         ueid = self.session.user.eid
-        affaire = self.execute('Affaire X WHERE X ref "AFFREF"').get_entity(0, 0)
-        self.execute('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR (X require_permission P?, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s',
+        affaire = self.sexecute('Affaire X WHERE X ref "AFFREF"').get_entity(0, 0)
+        self.sexecute('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR (X require_permission P?, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s',
                      {'x': affaire.eid, 'u': ueid})
 
     def test_nonregr2(self):
         treid = self.session.user.latest_trinfo().eid
-        rset = self.execute('Any X ORDERBY D DESC WHERE E eid %(x)s, E wf_info_for X, X modification_date D',
+        rset = self.sexecute('Any X ORDERBY D DESC WHERE E eid %(x)s, E wf_info_for X, X modification_date D',
                             {'x': treid})
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset.rows[0], [self.session.user.eid])
 
     def test_nonregr3(self):
-        self.execute('DELETE Card X WHERE X eid %(x)s, NOT X multisource_inlined_rel Y', {'x': self.ic1})
+        self.sexecute('DELETE Card X WHERE X eid %(x)s, NOT X multisource_inlined_rel Y', {'x': self.ic1})
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/server/test/unittest_querier.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_querier.py	Fri Aug 14 00:02:08 2009 +0200
@@ -46,7 +46,7 @@
                           ('C0 text,C1 integer', {'A': 'table0.C0', 'B': 'table0.C1'}))
 
 
-repo, cnx = init_test_database('sqlite')
+repo, cnx = init_test_database()
 
 
 
--- a/server/test/unittest_repository.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_repository.py	Fri Aug 14 00:02:08 2009 +0200
@@ -18,10 +18,11 @@
 
 from yams.constraints import UniqueConstraint
 
-from cubicweb import BadConnectionId, RepositoryError, ValidationError, UnknownEid, AuthenticationError
+from cubicweb import (BadConnectionId, RepositoryError, ValidationError,
+                      UnknownEid, AuthenticationError)
 from cubicweb.schema import CubicWebSchema, RQLConstraint
 from cubicweb.dbapi import connect, repo_connect, multiple_connections_unfix
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.repotest import tuplify
 from cubicweb.server import repository
 from cubicweb.server.sqlutils import SQL_PREFIX
@@ -31,48 +32,35 @@
 os.system('pyro-ns >/dev/null 2>/dev/null &')
 
 
-class RepositoryTC(RepositoryBasedTC):
+class RepositoryTC(CubicWebTC):
     """ singleton providing access to a persistent storage for entities
     and relation
     """
 
-#     def setUp(self):
-#         pass
-
-#     def tearDown(self):
-#         self.repo.config.db_perms = True
-#         cnxid = self.repo.connect(*self.default_user_password())
-#         for etype in ('Affaire', 'Note', 'Societe', 'Personne'):
-#             self.repo.execute(cnxid, 'DELETE %s X' % etype)
-#             self.repo.commit(cnxid)
-#         self.repo.close(cnxid)
-
     def test_fill_schema(self):
         self.repo.schema = CubicWebSchema(self.repo.config.appid)
         self.repo.config._cubes = None # avoid assertion error
+        self.repo.config.repairing = True # avoid versions checking
         self.repo.fill_schema()
-        pool = self.repo._get_pool()
         table = SQL_PREFIX + 'CWEType'
         namecol = SQL_PREFIX + 'name'
         finalcol = SQL_PREFIX + 'final'
-        try:
-            cu = self.session.system_sql('SELECT %s FROM %s WHERE %s is NULL' % (
-                namecol, table, finalcol))
-            self.assertEquals(cu.fetchall(), [])
-            cu = self.session.system_sql('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
-                              % (namecol, table, finalcol, namecol), {'final': 'TRUE'})
-            self.assertEquals(cu.fetchall(), [(u'Boolean',), (u'Bytes',),
-                                                     (u'Date',), (u'Datetime',),
-                                                     (u'Decimal',),(u'Float',),
-                                                     (u'Int',),
-                                                     (u'Interval',), (u'Password',),
-                                                     (u'String',), (u'Time',)])
-        finally:
-            self.repo._free_pool(pool)
+        self.session.set_pool()
+        cu = self.session.system_sql('SELECT %s FROM %s WHERE %s is NULL' % (
+            namecol, table, finalcol))
+        self.assertEquals(cu.fetchall(), [])
+        cu = self.session.system_sql('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
+                          % (namecol, table, finalcol, namecol), {'final': 'TRUE'})
+        self.assertEquals(cu.fetchall(), [(u'Boolean',), (u'Bytes',),
+                                          (u'Date',), (u'Datetime',),
+                                          (u'Decimal',),(u'Float',),
+                                          (u'Int',),
+                                          (u'Interval',), (u'Password',),
+                                          (u'String',), (u'Time',)])
 
     def test_schema_has_owner(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         self.failIf(repo.execute(cnxid, 'CWEType X WHERE NOT X owned_by U'))
         self.failIf(repo.execute(cnxid, 'CWRType X WHERE NOT X owned_by U'))
         self.failIf(repo.execute(cnxid, 'CWAttribute X WHERE NOT X owned_by U'))
@@ -81,18 +69,17 @@
         self.failIf(repo.execute(cnxid, 'CWConstraintType X WHERE NOT X owned_by U'))
 
     def test_connect(self):
-        login, passwd = self.default_user_password()
-        self.assert_(self.repo.connect(login, passwd))
+        self.assert_(self.repo.connect(self.admlogin, self.admpassword))
         self.assertRaises(AuthenticationError,
-                          self.repo.connect, login, 'nimportnawak')
+                          self.repo.connect, self.admlogin, 'nimportnawak')
         self.assertRaises(AuthenticationError,
-                          self.repo.connect, login, None)
+                          self.repo.connect, self.admlogin, None)
         self.assertRaises(AuthenticationError,
                           self.repo.connect, None, None)
 
     def test_execute(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         repo.execute(cnxid, 'Any X')
         repo.execute(cnxid, 'Any X where X is Personne')
         repo.execute(cnxid, 'Any X where X is Personne, X nom ~= "to"')
@@ -101,7 +88,7 @@
 
     def test_login_upassword_accent(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         repo.execute(cnxid, 'INSERT CWUser X: X login %(login)s, X upassword %(passwd)s, X in_state S, X in_group G WHERE S name "activated", G name "users"',
                      {'login': u"barnabé", 'passwd': u"héhéhé".encode('UTF8')})
         repo.commit(cnxid)
@@ -110,7 +97,7 @@
 
     def test_invalid_entity_rollback(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         # no group
         repo.execute(cnxid, 'INSERT CWUser X: X login %(login)s, X upassword %(passwd)s, X in_state S WHERE S name "activated"',
                      {'login': u"tutetute", 'passwd': 'tutetute'})
@@ -120,7 +107,7 @@
 
     def test_close(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         self.assert_(cnxid)
         repo.close(cnxid)
         self.assertRaises(BadConnectionId, repo.execute, cnxid, 'Any X')
@@ -131,9 +118,9 @@
 
     def test_shared_data(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         repo.set_shared_data(cnxid, 'data', 4)
-        cnxid2 = repo.connect(*self.default_user_password())
+        cnxid2 = repo.connect(self.admlogin, self.admpassword)
         self.assertEquals(repo.get_shared_data(cnxid, 'data'), 4)
         self.assertEquals(repo.get_shared_data(cnxid2, 'data'), None)
         repo.set_shared_data(cnxid2, 'data', 5)
@@ -151,14 +138,14 @@
 
     def test_check_session(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         self.assertEquals(repo.check_session(cnxid), None)
         repo.close(cnxid)
         self.assertRaises(BadConnectionId, repo.check_session, cnxid)
 
     def test_transaction_base(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         # check db state
         result = repo.execute(cnxid, 'Personne X')
         self.assertEquals(result.rowcount, 0)
@@ -177,7 +164,7 @@
 
     def test_transaction_base2(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         # rollback relation insertion
         repo.execute(cnxid, "SET U in_group G WHERE U login 'admin', G name 'guests'")
         result = repo.execute(cnxid, "Any U WHERE U in_group G, U login 'admin', G name 'guests'")
@@ -188,7 +175,7 @@
 
     def test_transaction_base3(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         # rollback state change which trigger TrInfo insertion
         ueid = repo._get_session(cnxid).user.eid
         rset = repo.execute(cnxid, 'TrInfo T WHERE T wf_info_for X, X eid %(x)s', {'x': ueid})
@@ -206,7 +193,7 @@
 
     def test_close_wait_processing_request(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         repo.execute(cnxid, 'INSERT CWUser X: X login "toto", X upassword "tutu", X in_group G WHERE G name "users"')
         repo.commit(cnxid)
         # close has to be in the thread due to sqlite limitations
@@ -290,7 +277,7 @@
 
     def test_internal_api(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         session = repo._get_session(cnxid, setpool=True)
         self.assertEquals(repo.type_and_source_from_eid(1, session),
                           ('CWGroup', 'system', None))
@@ -308,7 +295,7 @@
 
     def test_session_api(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         self.assertEquals(repo.user_info(cnxid), (5, 'admin', set([u'managers']), {}))
         self.assertEquals(repo.describe(cnxid, 1), (u'CWGroup', u'system', None))
         repo.close(cnxid)
@@ -317,7 +304,7 @@
 
     def test_shared_data_api(self):
         repo = self.repo
-        cnxid = repo.connect(*self.default_user_password())
+        cnxid = repo.connect(self.admlogin, self.admpassword)
         self.assertEquals(repo.get_shared_data(cnxid, 'data'), None)
         repo.set_shared_data(cnxid, 'data', 4)
         self.assertEquals(repo.get_shared_data(cnxid, 'data'), 4)
@@ -343,37 +330,34 @@
 #         print 'test time: %.3f (time) %.3f (cpu)' % ((time() - t), clock() - c)
 
 
-class DataHelpersTC(RepositoryBasedTC):
-
-    def setUp(self):
-        """ called before each test from this class """
-        cnxid = self.repo.connect(*self.default_user_password())
-        self.session = self.repo._sessions[cnxid]
-        self.session.set_pool()
-
-    def tearDown(self):
-        self.session.rollback()
+class DataHelpersTC(CubicWebTC):
 
     def test_create_eid(self):
+        self.session.set_pool()
         self.assert_(self.repo.system_source.create_eid(self.session))
 
     def test_source_from_eid(self):
+        self.session.set_pool()
         self.assertEquals(self.repo.source_from_eid(1, self.session),
                           self.repo.sources_by_uri['system'])
 
     def test_source_from_eid_raise(self):
+        self.session.set_pool()
         self.assertRaises(UnknownEid, self.repo.source_from_eid, -2, self.session)
 
     def test_type_from_eid(self):
+        self.session.set_pool()
         self.assertEquals(self.repo.type_from_eid(1, self.session), 'CWGroup')
 
     def test_type_from_eid_raise(self):
+        self.session.set_pool()
         self.assertRaises(UnknownEid, self.repo.type_from_eid, -2, self.session)
 
     def test_add_delete_info(self):
         entity = self.repo.vreg['etypes'].etype_class('Personne')(self.session)
         entity.eid = -1
         entity.complete = lambda x: None
+        self.session.set_pool()
         self.repo.add_info(self.session, entity, self.repo.sources_by_uri['system'])
         cu = self.session.system_sql('SELECT * FROM entities WHERE eid = -1')
         data = cu.fetchall()
@@ -388,13 +372,14 @@
         self.assertEquals(data, [])
 
 
-class FTITC(RepositoryBasedTC):
+class FTITC(CubicWebTC):
 
     def test_reindex_and_modified_since(self):
         eidp = self.execute('INSERT Personne X: X nom "toto", X prenom "tutu"')[0][0]
         self.commit()
         ts = datetime.now()
         self.assertEquals(len(self.execute('Personne X WHERE X has_text "tutu"')), 1)
+        self.session.set_pool()
         cu = self.session.system_sql('SELECT mtime, eid FROM entities WHERE eid = %s' % eidp)
         omtime = cu.fetchone()[0]
         # our sqlite datetime adapter is ignore seconds fraction, so we have to
@@ -403,6 +388,7 @@
         self.execute('SET X nom "tata" WHERE X eid %(x)s', {'x': eidp}, 'x')
         self.commit()
         self.assertEquals(len(self.execute('Personne X WHERE X has_text "tutu"')), 1)
+        self.session.set_pool()
         cu = self.session.system_sql('SELECT mtime FROM entities WHERE eid = %s' % eidp)
         mtime = cu.fetchone()[0]
         self.failUnless(omtime < mtime)
@@ -440,7 +426,7 @@
         self.assertEquals(rset.rows, [[self.session.user.eid]])
 
 
-class DBInitTC(RepositoryBasedTC):
+class DBInitTC(CubicWebTC):
 
     def test_versions_inserted(self):
         inserted = [r[0] for r in self.execute('Any K ORDERBY K WHERE P pkey K, P pkey ~= "system.version.%"')]
@@ -450,11 +436,11 @@
                            u'system.version.file', u'system.version.folder',
                            u'system.version.tag'])
 
-class InlineRelHooksTC(RepositoryBasedTC):
+class InlineRelHooksTC(CubicWebTC):
     """test relation hooks are called for inlined relations
     """
     def setUp(self):
-        RepositoryBasedTC.setUp(self)
+        CubicWebTC.setUp(self)
         self.hm = self.repo.hm
         self.called = []
 
--- a/server/test/unittest_rqlannotation.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_rqlannotation.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,7 +6,7 @@
 from cubicweb.devtools import init_test_database
 from cubicweb.devtools.repotest import BaseQuerierTC
 
-repo, cnx = init_test_database('sqlite')
+repo, cnx = init_test_database()
 
 class SQLGenAnnotatorTC(BaseQuerierTC):
     repo = repo
--- a/server/test/unittest_security.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_security.py	Fri Aug 14 00:02:08 2009 +0200
@@ -4,21 +4,21 @@
 import sys
 
 from logilab.common.testlib import unittest_main, TestCase
-from cubicweb.devtools.apptest import RepositoryBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb import Unauthorized, ValidationError
 from cubicweb.server.querier import check_read_access
 
-class BaseSecurityTC(RepositoryBasedTC):
+class BaseSecurityTC(CubicWebTC):
 
     def setUp(self):
-        RepositoryBasedTC.setUp(self)
+        CubicWebTC.setUp(self)
         self.create_user('iaminusersgrouponly')
         self.readoriggroups = self.schema['Personne'].get_groups('read')
         self.addoriggroups = self.schema['Personne'].get_groups('add')
 
     def tearDown(self):
-        RepositoryBasedTC.tearDown(self)
+        CubicWebTC.tearDown(self)
         self.schema['Personne'].set_groups('read', self.readoriggroups)
         self.schema['Personne'].set_groups('add', self.addoriggroups)
 
@@ -37,7 +37,7 @@
         cu = cnx.cursor()
         self.assertRaises(Unauthorized,
                           check_read_access,
-                          self.schema, cnx.user(self.current_session()), rqlst, solution)
+                          self.schema, cnx.user(self.session), rqlst, solution)
         self.assertRaises(Unauthorized, cu.execute, rql)
 
     def test_upassword_not_selectable(self):
@@ -165,7 +165,7 @@
 
     def test_insert_relation_rql_permission(self):
         cnx = self.login('iaminusersgrouponly')
-        session = self.current_session()
+        session = self.session
         cu = cnx.cursor(session)
         cu.execute("SET A concerne S WHERE A is Affaire, S is Societe")
         # should raise Unauthorized since user don't own S
@@ -210,7 +210,7 @@
 
 
     def test_user_can_change_its_upassword(self):
-        ueid = self.create_user('user')
+        ueid = self.create_user('user').eid
         cnx = self.login('user')
         cu = cnx.cursor()
         cu.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
@@ -220,7 +220,7 @@
         cnx = self.login('user', 'newpwd')
 
     def test_user_cant_change_other_upassword(self):
-        ueid = self.create_user('otheruser')
+        ueid = self.create_user('otheruser').eid
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
         cu.execute('SET X upassword %(passwd)s WHERE X eid %(x)s',
@@ -416,7 +416,7 @@
 
     def test_users_and_groups_non_readable_by_guests(self):
         cnx = self.login('anon')
-        anon = cnx.user(self.current_session())
+        anon = cnx.user(self.session)
         cu = cnx.cursor()
         # anonymous user can only read itself
         rset = cu.execute('Any L WHERE X owned_by U, U login L')
@@ -426,7 +426,7 @@
         # anonymous user can read groups (necessary to check allowed transitions for instance)
         self.assert_(cu.execute('CWGroup X'))
         # should only be able to read the anonymous user, not another one
-        origuser = self.session.user
+        origuser = self.adminsession.user
         self.assertRaises(Unauthorized,
                           cu.execute, 'CWUser X WHERE X eid %(x)s', {'x': origuser.eid}, 'x')
         # nothing selected, nothing updated, no exception raised
@@ -462,7 +462,7 @@
         self.commit()
         cnx = self.login('anon')
         cu = cnx.cursor()
-        anoneid = self.current_session().user.eid
+        anoneid = self.session.user.eid
         self.assertEquals(cu.execute('Any T,P ORDERBY lower(T) WHERE B is Bookmark,B title T,B path P,'
                                      'B bookmarked_by U, U eid %s' % anoneid).rows,
                           [['index', '?vid=index']])
@@ -491,7 +491,7 @@
         eid = self.execute('INSERT Affaire X: X ref "ARCT01"')[0][0]
         self.commit()
         cnx = self.login('iaminusersgrouponly')
-        session = self.current_session()
+        session = self.session
         # needed to avoid check_perm error
         session.set_pool()
         # needed to remove rql expr granting update perm to the user
@@ -506,7 +506,7 @@
         # XXX wether it should raise Unauthorized or ValidationError is not clear
         # the best would probably ValidationError if the transition doesn't exist
         # 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')
+        self.assertRaises(ValidationError, cu.execute, rql, {'x': cnx.user(self.session).eid}, 'x')
 
     def test_trinfo_security(self):
         aff = self.execute('INSERT Affaire X: X ref "ARCT01"').get_entity(0, 0)
--- a/server/test/unittest_ssplanner.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/server/test/unittest_ssplanner.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,7 +10,7 @@
 from cubicweb.server.ssplanner import SSPlanner
 
 # keep cnx so it's not garbage collected and the associated session is closed
-repo, cnx = init_test_database('sqlite')
+repo, cnx = init_test_database()
 
 class SSPlannerTC(BasePlannerTC):
     repo = repo
--- a/sobjects/notification.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/sobjects/notification.py	Fri Aug 14 00:02:08 2009 +0200
@@ -42,7 +42,7 @@
                 'X primary_email E, E address A')
 
     def recipients(self):
-        mode = self.config['default-recipients-mode']
+        mode = self.req.vreg.config['default-recipients-mode']
         if mode == 'users':
             # use unsafe execute else we may don't have the right to see users
             # to notify...
@@ -51,7 +51,7 @@
                      for u in execute(self.user_rql, build_descr=True, propagate=True).entities()]
         elif mode == 'default-dest-addrs':
             lang = self.vreg.property_value('ui.language')
-            dests = zip(self.config['default-dest-addrs'], repeat(lang))
+            dests = zip(self.req.vreg.config['default-dest-addrs'], repeat(lang))
         else: # mode == 'none'
             dests = []
         return dests
@@ -143,7 +143,7 @@
         return finder.recipients()
 
     def subject(self):
-        entity = self.entity(self.row or 0, self.col or 0)
+        entity = self.rset.get_entity(self.row or 0, self.col or 0)
         subject = self.req._(self.message)
         etype = entity.dc_type()
         eid = entity.eid
@@ -156,7 +156,7 @@
         return self.req.actual_session().user.login
 
     def context(self, **kwargs):
-        entity = self.entity(self.row or 0, self.col or 0)
+        entity = self.rset.get_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)
@@ -171,7 +171,7 @@
         self.w(self.req._(self.content) % self.context(**kwargs))
 
     def construct_message_id(self, eid):
-        return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
+        return construct_message_id(self.req.vreg.config.appid, eid, self.msgid_timestamp)
 
     def render_and_send(self, **kwargs):
         """generate and send an email message for this view"""
@@ -187,7 +187,7 @@
             lang = self.vreg.property_value('ui.language')
             recipients = zip(recipients, repeat(lang))
         if self.rset is not None:
-            entity = self.entity(self.row or 0, self.col or 0)
+            entity = self.rset.get_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:
@@ -211,7 +211,7 @@
             content = self.render(row=0, col=0, **kwargs)
             subject = self.subject()
             msg = format_mail(userdata, [emailaddr], content, subject,
-                              config=self.config, msgid=msgid, references=refs)
+                              config=self.req.vreg.config, msgid=msgid, references=refs)
             self.send([emailaddr], msg)
         # restore language
         self.req.set_language(origlang)
@@ -284,7 +284,7 @@
 """
 
     def context(self, **kwargs):
-        entity = self.entity(self.row or 0, self.col or 0)
+        entity = self.rset.get_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')
@@ -292,7 +292,7 @@
         return super(ContentAddedView, self).context(content=content, **kwargs)
 
     def subject(self):
-        entity = self.entity(self.row or 0, self.col or 0)
+        entity = self.rset.get_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 Aug 14 00:01:12 2009 +0200
+++ b/sobjects/supervising.py	Fri Aug 14 00:02:08 2009 +0200
@@ -23,7 +23,7 @@
     accepts = ('Any',)
 
     def call(self, session, *args):
-        dest = self.config['supervising-addrs']
+        dest = session.vreg.config['supervising-addrs']
         if not dest: # no supervisors, don't do this for nothing...
             return
         self.session = session
--- a/sobjects/test/unittest_email.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/sobjects/test/unittest_email.py	Fri Aug 14 00:02:08 2009 +0200
@@ -5,9 +5,9 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
-class EmailAddressHooksTC(EnvBasedTC):
+class EmailAddressHooksTC(CubicWebTC):
 
     def test_use_email_set_primary_email(self):
         self.execute('INSERT EmailAddress X: X address "admin@logilab.fr", U use_email X WHERE U login "admin"')
--- a/sobjects/test/unittest_hooks.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/sobjects/test/unittest_hooks.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,9 +6,9 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
-class HooksTC(EnvBasedTC):
+class HooksTC(CubicWebTC):
 
     def test_euser_login_stripped(self):
         u = self.create_user('  joe  ')
--- a/sobjects/test/unittest_notification.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/sobjects/test/unittest_notification.py	Fri Aug 14 00:02:08 2009 +0200
@@ -9,7 +9,7 @@
 from socket import gethostname
 
 from logilab.common.testlib import unittest_main, TestCase
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.sobjects.notification import construct_message_id, parse_message_id
 
@@ -48,7 +48,7 @@
             self.assertNotEquals(msgid1, '<@testapp.%s>' % gethostname())
 
 
-class RecipientsFinderTC(EnvBasedTC):
+class RecipientsFinderTC(CubicWebTC):
     def test(self):
         urset = self.execute('CWUser X WHERE X login "admin"')
         self.execute('INSERT EmailAddress X: X address "admin@logilab.fr", U primary_email X '
@@ -67,10 +67,10 @@
         self.assertEquals(finder.recipients(), [('abcd@logilab.fr', 'en'), ('efgh@logilab.fr', 'en')])
 
 
-class StatusChangeViewsTC(EnvBasedTC):
+class StatusChangeViewsTC(CubicWebTC):
 
     def test_status_change_view(self):
-        req = self.session()
+        req = self.session
         u = self.create_user('toto', req=req)
         assert u.req
         assert u.rset
--- a/sobjects/test/unittest_supervising.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/sobjects/test/unittest_supervising.py	Fri Aug 14 00:02:08 2009 +0200
@@ -9,12 +9,12 @@
 import re
 
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.sobjects.supervising import SendMailOp, SupervisionMailOp
 
 
-class SupervisingTC(EnvBasedTC):
+class SupervisingTC(CubicWebTC):
 
     def setup_database(self):
         self.add_entity('Card', title=u"une news !", content=u"cubicweb c'est beau")
@@ -26,7 +26,7 @@
 
 
     def test_supervision(self):
-        session = self.session()
+        session = self.session
         # do some modification
         ueid = self.execute('INSERT CWUser X: X login "toto", X upassword "sosafe", X in_group G, X in_state S '
                             'WHERE G name "users", S name "activated"')[0][0]
@@ -75,7 +75,7 @@
         self.assertEquals(op.to_send[0][1], ['test@logilab.fr'])
 
     def test_nonregr1(self):
-        session = self.session()
+        session = self.session
         # do some unlogged modification
         self.execute('SET X last_login_time NOW WHERE X eid %(x)s', {'x': session.user.eid}, 'x')
         self.commit() # no crash
--- a/test/unittest_dbapi.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/test/unittest_dbapi.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,24 +7,21 @@
 """
 from cubicweb import ConnectionError
 from cubicweb.dbapi import ProgrammingError
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 
-class DBAPITC(EnvBasedTC):
-    @property
-    def cnx(self):
-        return self.login('anon')
+class DBAPITC(CubicWebTC):
 
     def test_public_repo_api(self):
-        cnx = self.cnx
-        self.assertEquals(cnx.get_schema(), self.env.repo.schema)
+        cnx = self.login('anon')
+        self.assertEquals(cnx.get_schema(), self.repo.schema)
         self.assertEquals(cnx.source_defs(), {'system': {'adapter': 'native', 'uri': 'system'}})
         self.restore_connection() # proper way to close cnx
         self.assertRaises(ProgrammingError, cnx.get_schema)
         self.assertRaises(ProgrammingError, cnx.source_defs)
 
     def test_db_api(self):
-        cnx = self.cnx
+        cnx = self.login('anon')
         self.assertEquals(cnx.rollback(), None)
         self.assertEquals(cnx.commit(), None)
         self.restore_connection() # proper way to close cnx
@@ -34,7 +31,7 @@
         self.assertRaises(ProgrammingError, cnx.close)
 
     def test_api(self):
-        cnx = self.cnx
+        cnx = self.login('anon')
         self.assertEquals(cnx.user(None).login, 'anon')
         self.assertEquals(cnx.describe(1), (u'CWGroup', u'system', None))
         self.restore_connection() # proper way to close cnx
@@ -42,7 +39,7 @@
         self.assertRaises(ConnectionError, cnx.describe, 1)
 
     def test_session_data_api(self):
-        cnx = self.cnx
+        cnx = self.login('anon')
         self.assertEquals(cnx.get_session_data('data'), None)
         self.assertEquals(cnx.session_data(), {})
         cnx.set_session_data('data', 4)
@@ -57,7 +54,7 @@
         self.assertEquals(cnx.session_data(), {'data': 4})
 
     def test_shared_data_api(self):
-        cnx = self.cnx
+        cnx = self.login('anon')
         self.assertEquals(cnx.get_shared_data('data'), None)
         cnx.set_shared_data('data', 4)
         self.assertEquals(cnx.get_shared_data('data'), 4)
@@ -71,19 +68,6 @@
         self.assertRaises(ConnectionError, cnx.set_shared_data, 'data', 0)
         self.assertRaises(ConnectionError, cnx.get_shared_data, 'data')
 
-
-# class DBAPICursorTC(EnvBasedTC):
-
-#     @property
-#     def cursor(self):
-#         return self.env.cnx.cursor()
-
-#     def test_api(self):
-#         cu = self.cursor
-#         self.assertEquals(cu.describe(1), (u'CWGroup', u'system', None))
-#         #cu.close()
-#         #self.assertRaises(ConnectionError, cu.describe, 1)
-
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/test/unittest_entity.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/test/unittest_entity.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,10 +10,10 @@
 from datetime import datetime
 
 from cubicweb import Binary
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.common.mttransforms import HAS_TAL
 
-class EntityTC(EnvBasedTC):
+class EntityTC(CubicWebTC):
 
 ##     def setup_database(self):
 ##         self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
@@ -23,18 +23,18 @@
 ##                         embed=False)
 
     def test_boolean_value(self):
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         self.failUnless(e)
 
     def test_yams_inheritance(self):
         from entities import Note
-        e = self.etype_instance('SubNote')
+        e = self.vreg['etypes'].etype_class('SubNote')(self.request())
         self.assertIsInstance(e, Note)
-        e2 = self.etype_instance('SubNote')
+        e2 = self.vreg['etypes'].etype_class('SubNote')(self.request())
         self.assertIs(e.__class__, e2.__class__)
 
     def test_has_eid(self):
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         self.assertEquals(e.eid, None)
         self.assertEquals(e.has_eid(), False)
         e.eid = 'X'
@@ -208,7 +208,7 @@
                           1)
 
     def test_new_entity_unrelated(self):
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         unrelated = [r[0] for r in e.unrelated('in_group', 'CWGroup', 'subject')]
         # should be default groups but owners, i.e. managers, users, guests
         self.assertEquals(len(unrelated), 3)
@@ -227,7 +227,6 @@
         self.assertEquals(e.printable_value('content'),
                           '<p>\ndu *texte*\n</p>')
         e['title'] = 'zou'
-        #e = self.etype_instance('Task')
         e['content'] = '''\
 a title
 =======
@@ -303,7 +302,7 @@
 
 
     def test_fulltextindex(self):
-        e = self.etype_instance('File')
+        e = self.vreg['etypes'].etype_class('File')(self.request())
         e['name'] = 'an html file'
         e['description'] = 'du <em>html</em>'
         e['description_format'] = 'text/html'
@@ -324,7 +323,7 @@
     def test_complete_relation(self):
         self.execute('SET RT add_permission G WHERE RT name "wf_info_for", G name "managers"')
         self.commit()
-        session = self.session()
+        session = self.session
         try:
             eid = session.unsafe_execute(
                 'INSERT TrInfo X: X comment "zou", X wf_info_for U, X from_state S1, X to_state S2 '
--- a/test/unittest_rset.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/test/unittest_rset.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,7 +10,7 @@
 
 from logilab.common.testlib import TestCase, unittest_main
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.selectors import traced_selection
 
 from urlparse import urlsplit
@@ -55,7 +55,7 @@
 
 
 
-class ResultSetTC(EnvBasedTC):
+class ResultSetTC(CubicWebTC):
 
     def setUp(self):
         super(ResultSetTC, self).setUp()
@@ -100,7 +100,7 @@
                        'Any U,L where U is CWUser, U login L',
                        description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
-        rs.vreg = self.env.vreg
+        rs.vreg = self.vreg
 
         self.assertEquals(rs.limit(2).rows, [[12000, 'adim'], [13000, 'syt']])
         rs2 = rs.limit(2, offset=1)
@@ -115,7 +115,7 @@
                        'Any U,L where U is CWUser, U login L',
                        description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
-        rs.vreg = self.env.vreg
+        rs.vreg = self.vreg
         def test_filter(entity):
             return entity.login != 'nico'
 
@@ -140,7 +140,7 @@
                        'Any U,L where U is CWUser, U login L',
                        description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
-        rs.vreg = self.env.vreg
+        rs.vreg = self.vreg
 
         rs2 = rs.sorted_rset(lambda e:e['login'])
         self.assertEquals(len(rs2), 3)
@@ -170,7 +170,7 @@
                        'D created_by U, D title T',
                        description=[['CWUser', 'String', 'String']] * 5)
         rs.req = self.request()
-        rs.vreg = self.env.vreg
+        rs.vreg = self.vreg
 
         rsets = rs.split_rset(lambda e:e['login'])
         self.assertEquals(len(rsets), 3)
--- a/test/unittest_selectors.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/test/unittest_selectors.py	Fri Aug 14 00:02:08 2009 +0200
@@ -8,7 +8,7 @@
 
 from logilab.common.testlib import TestCase, unittest_main
 
-from cubicweb.devtools.testlib import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.appobject import Selector, AndSelector, OrSelector
 from cubicweb.selectors import implements, match_user_groups
 from cubicweb.interfaces import IDownloadable
@@ -88,7 +88,7 @@
         self.assertIs(csel.search_selector(implements), sel)
 
 
-class ImplementsSelectorTC(EnvBasedTC):
+class ImplementsSelectorTC(CubicWebTC):
     def test_etype_priority(self):
         req = self.request()
         cls = self.vreg['etypes'].etype_class('File')
@@ -103,7 +103,7 @@
         self.failIf(implements('Societe').score_class(cls, self.request()))
 
 
-class MatchUserGroupsTC(EnvBasedTC):
+class MatchUserGroupsTC(CubicWebTC):
     def test_owners_group(self):
         """tests usage of 'owners' group with match_user_group"""
         class SomeAction(action.Action):
@@ -118,16 +118,16 @@
             self.create_user('john')
             self.login('john')
             # it should not be possible to use SomeAction not owned objects
-            rset, req = self.env.get_rset_and_req('Any G WHERE G is CWGroup, G name "managers"')
+            rset, req = self.rset_and_req('Any G WHERE G is CWGroup, G name "managers"')
             self.failIf('yo' in dict(self.pactions(req, rset)))
             # insert a new card, and check that we can use SomeAction on our object
             self.execute('INSERT Card C: C title "zoubidou"')
             self.commit()
-            rset, req = self.env.get_rset_and_req('Card C WHERE C title "zoubidou"')
+            rset, req = self.rset_and_req('Card C WHERE C title "zoubidou"')
             self.failUnless('yo' in dict(self.pactions(req, rset)), self.pactions(req, rset))
             # make sure even managers can't use the action
             self.restore_connection()
-            rset, req = self.env.get_rset_and_req('Card C WHERE C title "zoubidou"')
+            rset, req = self.rset_and_req('Card C WHERE C title "zoubidou"')
             self.failIf('yo' in dict(self.pactions(req, rset)))
         finally:
             del self.vreg[SomeAction.__registry__][SomeAction.id]
--- a/test/unittest_vregistry.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/test/unittest_vregistry.py	Fri Aug 14 00:02:08 2009 +0200
@@ -40,14 +40,6 @@
         self.vreg.initialization_completed()
         self.assertEquals(len(self.vreg['views']['primary']), 1)
 
-    def test___selectors__compat(self):
-        myselector1 = lambda *args: 1
-        myselector2 = lambda *args: 1
-        class AnAppObject(AppObject):
-            __selectors__ = (myselector1, myselector2)
-        AnAppObject.build___select__()
-        self.assertEquals(AnAppObject.__select__(AnAppObject), 2)
-
     def test_properties(self):
         self.failIf('system.version.cubicweb' in self.vreg['propertydefs'])
         self.failUnless(self.vreg.property_info('system.version.cubicweb'))
--- a/toolsutils.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/toolsutils.py	Fri Aug 14 00:02:08 2009 +0200
@@ -21,6 +21,9 @@
 from cubicweb import warning
 from cubicweb import ConfigurationError, ExecutionError
 
+def underline_title(title, car='-'):
+    return title+'\n'+(car*len(title))
+
 def iter_dir(directory, condition_file=None, ignore=()):
     """iterate on a directory"""
     for sub in listdir(directory):
--- a/view.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/view.py	Fri Aug 14 00:02:08 2009 +0200
@@ -17,7 +17,6 @@
 
 from cubicweb import NotAnEntity
 from cubicweb.selectors import yes, non_final_entity, nonempty_rset, none_rset
-from cubicweb.selectors import require_group_compat, accepts_compat
 from cubicweb.appobject import AppObject
 from cubicweb.utils import UStringIO, HTMLStream
 from cubicweb.schema import display_name
@@ -93,7 +92,6 @@
     time to a write function to use.
     """
     __registry__ = 'views'
-    registered = require_group_compat(AppObject.registered)
 
     templatable = True
     need_navigation = True
@@ -103,7 +101,7 @@
     category = 'view'
 
     def __init__(self, req=None, rset=None, **kwargs):
-        super(View, self).__init__(req, rset, **kwargs)
+        super(View, self).__init__(req, rset=rset, **kwargs)
         self.w = None
 
     @property
@@ -151,7 +149,20 @@
         if stream is not None:
             return self._stream.getvalue()
 
-    dispatch = deprecated('.dispatch is deprecated, use .render')(render)
+    def tal_render(self, template, variables):
+        """render a precompiled page template with variables in the given
+        dictionary as context
+        """
+        from cubicweb.ext.tal import CubicWebContext
+        context = CubicWebContext()
+        context.update({'self': self, 'rset': self.rset, '_' : self.req._,
+                        'req': self.req, 'user': self.req.user})
+        context.update(variables)
+        output = UStringIO()
+        template.expand(context, output)
+        return output.getvalue()
+
+    dispatch = deprecated('[3.4] .dispatch is deprecated, use .render')(render)
 
     # should default .call() method add a <div classs="section"> around each
     # rset item
@@ -230,7 +241,7 @@
         self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
 
     # XXX Template bw compat
-    template = deprecated('.template is deprecated, use .view')(wview)
+    template = deprecated('[3.4] .template is deprecated, use .view')(wview)
 
     def whead(self, data):
         self.req.html_headers.write(data)
@@ -317,8 +328,6 @@
 class EntityView(View):
     """base class for views applying on an entity (i.e. uniform result set)"""
     __select__ = non_final_entity()
-    registered = accepts_compat(View.registered)
-
     category = 'entityview'
 
 
@@ -327,7 +336,6 @@
     displayed (so they can always be displayed !)
     """
     __select__ = none_rset()
-    registered = require_group_compat(View.registered)
 
     category = 'startupview'
 
@@ -400,7 +408,6 @@
     There is usually at least a regular main template and a simple fallback
     one to display error if the first one failed
     """
-    registered = require_group_compat(View.registered)
 
     @property
     def doctype(self):
@@ -466,10 +473,9 @@
     """base class for components"""
     __registry__ = 'components'
     __select__ = yes()
-    property_defs = {}
 
     def div_class(self):
-        return '%s %s' % (self.propval('htmlclass'), self.id)
+        return '%s %s' % (self.cw_propval('htmlclass'), self.id)
 
     def div_id(self):
         return '%sComponent' % self.id
--- a/vregistry.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/vregistry.py	Fri Aug 14 00:02:08 2009 +0200
@@ -54,6 +54,20 @@
     return _toload
 
 
+def classid(cls):
+    """returns a unique identifier for an appobject class"""
+    return '%s.%s' % (cls.__module__, cls.__name__)
+
+def class_regid(cls):
+    """returns a unique identifier for an appobject class"""
+    if 'id' in cls.__dict__:
+        warn('%s: id is deprecated, use __id__')
+        cls.__id__ = cls.id
+    if hasattr(cls, 'id'):
+        return cls.id
+    return cls.__id__
+
+
 class Registry(dict):
 
     def __init__(self, config):
@@ -72,15 +86,13 @@
     def register(self, obj, oid=None, clear=False):
         """base method to add an object in the registry"""
         assert not '__abstract__' in obj.__dict__
-        oid = oid or obj.id
+        oid = oid or class_regid(obj)
         assert oid
         if clear:
             appobjects = self[oid] =  []
         else:
             appobjects = self.setdefault(oid, [])
-        # registered() is technically a classmethod but is not declared
-        # as such because we need to compose registered in some cases
-        appobject = obj.registered.im_func(obj, self)
+        appobject = obj.__registered__(self)
         assert not appobject in appobjects, \
                'object %s is already registered' % appobject
         assert callable(appobject.__select__), appobject
@@ -90,11 +102,11 @@
         # XXXFIXME this is a duplication of unregister()
         # remove register_and_replace in favor of unregister + register
         # or simplify by calling unregister then register here
-        if hasattr(replaced, 'classid'):
-            replaced = replaced.classid()
-        registered_objs = self.get(obj.id, ())
+        if not isinstance(replaced, basestring):
+            replaced = classid(replaced)
+        registered_objs = self.get(class_regid(obj), ())
         for index, registered in enumerate(registered_objs):
-            if registered.classid() == replaced:
+            if classid(registered) == replaced:
                 del registered_objs[index]
                 break
         else:
@@ -103,17 +115,18 @@
         self.register(obj)
 
     def unregister(self, obj):
-        oid = obj.classid()
-        for registered in self.get(obj.id, ()):
+        clsid = classid(obj)
+        oid = class_regid(obj)
+        for registered in self.get(oid, ()):
             # use classid() to compare classes because vreg will probably
             # have its own version of the class, loaded through execfile
-            if registered.classid() == oid:
+            if classid(registered) == clsid:
                 # XXX automatic reloading management
-                self[obj.id].remove(registered)
+                self[oid].remove(registered)
                 break
         else:
             self.warning('can\'t remove %s, no id %s in the registry',
-                         oid, obj.id)
+                         clsid, oid)
 
     def all_objects(self):
         """return a list containing all objects in this registry.
@@ -125,6 +138,7 @@
 
     # dynamic selection methods ################################################
 
+    @deprecated('[3.5] use select instead of object_by_id')
     def object_by_id(self, oid, *args, **kwargs):
         """return object with the given oid. Only one object is expected to be
         found.
@@ -143,9 +157,9 @@
         raise `ObjectNotFound` if not object with id <oid> in <registry>
         raise `NoSelectableObject` if not object apply
         """
-        return self.select_best(self[oid], *args, **kwargs)
+        return self._select_best(self[oid], *args, **kwargs)
 
-    def select_object(self, oid, *args, **kwargs):
+    def select_or_none(self, oid, *args, **kwargs):
         """return the most specific object among those with the given oid
         according to the given context, or None if no object applies.
         """
@@ -153,6 +167,8 @@
             return self.select(oid, *args, **kwargs)
         except (NoSelectableObject, ObjectNotFound):
             return None
+    select_object = deprecated('[3.5] use select_or_none instead of select_object'
+                               )(select_or_none)
 
     def possible_objects(self, *args, **kwargs):
         """return an iterator on possible objects in this registry for the given
@@ -160,11 +176,11 @@
         """
         for appobjects in self.itervalues():
             try:
-                yield self.select_best(appobjects, *args, **kwargs)
+                yield self._select_best(appobjects, *args, **kwargs)
             except NoSelectableObject:
                 continue
 
-    def select_best(self, appobjects, *args, **kwargs):
+    def _select_best(self, appobjects, *args, **kwargs):
         """return an instance of the most specific object according
         to parameters
 
@@ -194,7 +210,7 @@
                                    [repr(v) for v in winners]))
         # return the result of calling the appobject
         return winners[0](*args, **kwargs)
-
+    select_best = deprecated('[3.5] select_best is now private')(_select_best)
 
 class VRegistry(dict):
     """class responsible to register, propose and select the various
@@ -221,7 +237,7 @@
 
     # dynamic selection methods ################################################
 
-    @deprecated('use vreg[registry].object_by_id(oid, *args, **kwargs)')
+    @deprecated('[3.4] use vreg[registry].object_by_id(oid, *args, **kwargs)')
     def object_by_id(self, registry, oid, *args, **kwargs):
         """return object in <registry>.<oid>
 
@@ -230,7 +246,7 @@
         """
         return self[registry].object_by_id(oid)
 
-    @deprecated('use vreg[registry].select(oid, *args, **kwargs)')
+    @deprecated('[3.4] use vreg[registry].select(oid, *args, **kwargs)')
     def select(self, registry, oid, *args, **kwargs):
         """return the most specific object in <registry>.<oid> according to
         the given context
@@ -240,14 +256,14 @@
         """
         return self[registry].select(oid, *args, **kwargs)
 
-    @deprecated('use vreg[registry].select_object(oid, *args, **kwargs)')
+    @deprecated('[3.4] use vreg[registry].select_or_none(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
         """
-        return self[registry].select_object(oid, *args, **kwargs)
+        return self[registry].select_or_none(oid, *args, **kwargs)
 
-    @deprecated('use vreg[registry].possible_objects(*args, **kwargs)')
+    @deprecated('[3.4] use vreg[registry].possible_objects(*args, **kwargs)')
     def possible_objects(self, registry, *args, **kwargs):
         """return an iterator on possible objects in <registry> for the given
         context
@@ -281,7 +297,7 @@
             try:
                 if obj.__module__ != modname or obj in butclasses:
                     continue
-                oid = obj.id
+                oid = class_regid(obj)
             except AttributeError:
                 continue
             if oid and not '__abstract__' in obj.__dict__:
@@ -415,10 +431,10 @@
         to a non empty string to be registered.
         """
         if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
-            or not cls.__registry__ or not cls.id):
+            or not cls.__registry__ or not class_regid(cls)):
             return
         regname = cls.__registry__
-        if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']:
+        if '%s.%s' % (regname, class_regid(cls)) in self.config['disable-appobjects']:
             return
         self.register(cls)
 
@@ -431,11 +447,11 @@
 
 from cubicweb.appobject import objectify_selector, AndSelector, OrSelector, Selector
 
-objectify_selector = deprecated('objectify_selector has been moved to appobject module')(objectify_selector)
+objectify_selector = deprecated('[3.4] objectify_selector has been moved to appobject module')(objectify_selector)
 
 Selector = class_moved(Selector)
 
-@deprecated('use & operator (binary and)')
+@deprecated('[3.4] use & operator (binary and)')
 def chainall(*selectors, **kwargs):
     """return a selector chaining given selectors. If one of
     the selectors fail, selection will fail, else the returned score
@@ -448,7 +464,7 @@
         selector.__name__ = kwargs['name']
     return selector
 
-@deprecated('use | operator (binary or)')
+@deprecated('[3.4] use | operator (binary or)')
 def chainfirst(*selectors, **kwargs):
     """return a selector chaining given selectors. If all
     the selectors fail, selection will fail, else the returned score
--- a/web/__init__.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/__init__.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,10 +13,10 @@
 from decimal import Decimal
 from datetime import datetime, date, timedelta
 from simplejson import dumps
+from urllib import quote as urlquote
 
 from logilab.common.deprecation import deprecated
 
-from cubicweb.common.uilib import urlquote
 from cubicweb.web._exceptions import *
 
 
@@ -65,7 +65,7 @@
         return json_dumps(function(*args, **kwargs))
     return newfunc
 
-@deprecated('use req.build_ajax_replace_url() instead')
+@deprecated('[3.4] use req.build_ajax_replace_url() instead')
 def ajax_replace_url(nodeid, rql, vid=None, swap=False, **extraparams):
     """builds a replacePageChunk-like url
     >>> ajax_replace_url('foo', 'Person P')
--- a/web/action.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/action.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,8 +10,7 @@
 
 from cubicweb import target
 from cubicweb.selectors import (partial_relation_possible, match_search_state,
-                                one_line_rset, partial_may_add_relation, yes,
-                                accepts_compat, condition_compat, deprecate)
+                                one_line_rset, partial_may_add_relation, yes)
 from cubicweb.appobject import AppObject
 
 
@@ -22,7 +21,7 @@
     __registry__ = 'actions'
     __select__ = match_search_state('normal')
 
-    property_defs = {
+    cw_property_defs = {
         'visible':  dict(type='Boolean', default=True,
                          help=_('display the action or not')),
         'order':    dict(type='Int', default=99,
@@ -73,8 +72,6 @@
     __select__ = (match_search_state('normal') & one_line_rset()
                   & partial_relation_possible(action='add')
                   & partial_may_add_relation())
-    registered = accepts_compat(Action.registered)
-
     category = 'addrelated'
 
     def url(self):
@@ -85,10 +82,3 @@
                               __redirectpath=current_entity.rest_path(), # should not be url quoted!
                               __redirectvid=self.req.form.get('__redirectvid', ''))
 
-class EntityAction(Action):
-    """DEPRECATED / BACKWARD COMPAT
-    """
-    registered = deprecate(condition_compat(accepts_compat(Action.registered)),
-                           msg='EntityAction is deprecated, use Action with '
-                           'appropriate selectors')
-
--- a/web/application.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/application.py	Fri Aug 14 00:02:08 2009 +0200
@@ -289,7 +289,7 @@
             finally:
                 self._logfile_lock.release()
 
-    @deprecated("use vreg.select('controllers', ...)")
+    @deprecated("[3.4] use vreg['controllers'].select(...)")
     def select_controller(self, oid, req):
         try:
             return self.vreg['controllers'].select(oid, req=req, appli=self)
--- a/web/box.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/box.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,9 +13,7 @@
 from cubicweb import Unauthorized, role as get_role, target as get_target
 from cubicweb.schema import display_name
 from cubicweb.selectors import (one_line_rset,  primary_view,
-                                match_context_prop, partial_has_related_entities,
-                                accepts_compat, has_relation_compat,
-                                condition_compat, require_group_compat)
+                                match_context_prop, partial_has_related_entities)
 from cubicweb.view import View, ReloadableMixIn
 
 from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
@@ -39,10 +37,9 @@
     """
     __registry__ = 'boxes'
     __select__ = match_context_prop()
-    registered = classmethod(require_group_compat(View.registered))
 
     categories_in_order = ()
-    property_defs = {
+    cw_property_defs = {
         _('visible'): dict(type='Boolean', default=True,
                            help=_('display the box or not')),
         _('order'):   dict(type='Int', default=99,
@@ -138,7 +135,6 @@
 class EntityBoxTemplate(BoxTemplate):
     """base class for boxes related to a single entity"""
     __select__ = BoxTemplate.__select__ & one_line_rset() & primary_view()
-    registered = accepts_compat(has_relation_compat(condition_compat(BoxTemplate.registered)))
     context = 'incontext'
 
     def call(self, row=0, col=0, **kwargs):
@@ -150,7 +146,7 @@
     __select__ = EntityBoxTemplate.__select__ & partial_has_related_entities()
 
     def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         limit = self.req.property_value('navigation.related-limit') + 1
         role = get_role(self)
         self.w(u'<div class="sideBox">')
@@ -169,7 +165,7 @@
 
     def cell_call(self, row, col, view=None, **kwargs):
         self.req.add_js('cubicweb.ajax.js')
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         box = SideBoxWidget(display_name(self.req, self.rtype), self.id)
         related = self.related_boxitems(entity)
         unrelated = self.unrelated_boxitems(entity)
--- a/web/component.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/component.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,11 +13,10 @@
 
 from cubicweb import role
 from cubicweb.utils import merge_dicts
-from cubicweb.view import View, Component
+from cubicweb.view import Component
 from cubicweb.selectors import (
     paginated_rset, one_line_rset, primary_view, match_context_prop,
-    partial_has_related_entities, condition_compat, accepts_compat,
-    has_relation_compat)
+    partial_has_related_entities)
 
 
 class EntityVComponent(Component):
@@ -33,9 +32,8 @@
 
     __registry__ = 'contentnavigation'
     __select__ = one_line_rset() & primary_view() & match_context_prop()
-    registered = accepts_compat(has_relation_compat(condition_compat(View.registered)))
 
-    property_defs = {
+    cw_property_defs = {
         _('visible'):  dict(type='Boolean', default=True,
                             help=_('display the component or not')),
         _('order'):    dict(type='Int', default=99,
@@ -63,7 +61,7 @@
     id = 'navigation'
     __select__ = paginated_rset()
 
-    property_defs = {
+    cw_property_defs = {
         _('visible'):  dict(type='Boolean', default=True,
                             help=_('display the component or not')),
         }
--- a/web/controller.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/controller.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,7 +11,7 @@
 import datetime
 
 from cubicweb import typed_eid
-from cubicweb.selectors import yes, require_group_compat
+from cubicweb.selectors import yes
 from cubicweb.appobject import AppObject
 from cubicweb.web import LOGGER, Redirect, RequestError
 
@@ -68,7 +68,6 @@
     """
     __registry__ = 'controllers'
     __select__ = yes()
-    registered = require_group_compat(AppObject.registered)
 
     def __init__(self, *args, **kwargs):
         self.appli = kwargs.pop('appli', None)
@@ -90,10 +89,10 @@
         # XXX assigning to self really necessary?
         self.rset = None
         if rql:
-            self.ensure_ro_rql(rql)
+            self.req.ensure_ro_rql(rql)
             if not isinstance(rql, unicode):
                 rql = unicode(rql, self.req.encoding)
-            pp = self.vreg['components'].select_object('magicsearch', self.req)
+            pp = self.vreg['components'].select_or_none('magicsearch', self.req)
             if pp is not None:
                 self.rset = pp.process_query(rql, self.req)
         return self.rset
--- a/web/facet.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/facet.py	Fri Aug 14 00:02:08 2009 +0200
@@ -64,8 +64,8 @@
 
 
 def get_facet(req, facetid, rqlst, mainvar):
-    return req.vreg['facets'].object_by_id(facetid, req, rqlst=rqlst,
-                                           filtered_variable=mainvar)
+    return req.vreg['facets'].select(facetid, req, rqlst=rqlst,
+                                     filtered_variable=mainvar)
 
 
 def filter_hiddens(w, **kwargs):
@@ -244,7 +244,7 @@
 class AbstractFacet(AppObject):
     __abstract__ = True
     __registry__ = 'facets'
-    property_defs = {
+    cw_property_defs = {
         _('visible'): dict(type='Boolean', default=True,
                            help=_('display the box or not')),
         _('order'):   dict(type='Int', default=99,
--- a/web/form.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/form.py	Fri Aug 14 00:02:08 2009 +0200
@@ -16,7 +16,6 @@
 class FormViewMixIn(object):
     """abstract form view mix-in"""
     category = 'form'
-    controller = 'edit'
     http_cache_manager = httpcache.NoHTTPCacheManager
     add_to_breadcrumbs = False
 
@@ -79,7 +78,6 @@
 
     domid = 'entityForm'
     category = 'form'
-    controller = 'edit'
     http_cache_manager = httpcache.NoHTTPCacheManager
     add_to_breadcrumbs = False
 
--- a/web/request.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/request.py	Fri Aug 14 00:02:08 2009 +0200
@@ -15,11 +15,12 @@
 from urlparse import urlsplit
 from itertools import count
 
+from simplejson import dumps
+
 from rql.utils import rqlvar_maker
 
 from logilab.common.decorators import cached
 from logilab.common.deprecation import deprecated
-
 from logilab.mtconverter import xml_escape
 
 from cubicweb.dbapi import DBAPIRequest
@@ -85,9 +86,16 @@
         self.next_tabindex = self.tabindexgen.next
         # page id, set by htmlheader template
         self.pageid = None
-        self.varmaker = rqlvar_maker()
         self.datadir_url = self._datadir_url()
 
+    @property
+    def varmaker(self):
+        varmaker = self.get_page_data('rql_varmaker')
+        if varmaker is None:
+            varmaker = rqlvar_maker()
+            self.set_page_data('rql_varmaker', varmaker)
+        return varmaker
+
     def set_connection(self, cnx, user=None):
         """method called by the session handler when the user is authenticated
         or an anonymous connection is open
@@ -257,6 +265,24 @@
             return breadcrumbs.pop()
         return self.base_url()
 
+    def user_rql_callback(self, args, msg=None):
+        """register a user callback to execute some rql query and return an url
+        to call it ready to be inserted in html
+        """
+        def rqlexec(req, rql, args=None, key=None):
+            req.execute(rql, args, key)
+        return self.user_callback(rqlexec, args, msg)
+
+    def user_callback(self, cb, args, msg=None, nonify=False):
+        """register the given user callback and return an url to call it ready to be
+        inserted in html
+        """
+        self.add_js('cubicweb.ajax.js')
+        cbname = self.register_onetime_callback(cb, *args)
+        msg = dumps(msg or '')
+        return "javascript:userCallbackThenReloadPage('%s', %s)" % (
+            cbname, msg)
+
     def register_onetime_callback(self, func, *args):
         cbname = 'cb_%s' % (
             sha.sha('%s%s%s%s' % (time.time(), func.__name__,
@@ -629,7 +655,7 @@
                            auth, ex.__class__.__name__, ex)
         return None, None
 
-    @deprecated("use parse_accept_header('Accept-Language')")
+    @deprecated("[3.4] use parse_accept_header('Accept-Language')")
     def header_accept_language(self):
         """returns an ordered list of preferred languages"""
         return [value.split('-')[0] for value in
--- a/web/test/test_views.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/test_views.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,7 +6,7 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
-from cubicweb.devtools.testlib import WebTest, AutomaticWebTest
+from cubicweb.devtools.testlib import CubicWebTC, AutoPopulateTest, AutomaticWebTest
 from cubicweb.view import AnyRsetView
 
 AutomaticWebTest.application_rql = [
@@ -15,7 +15,7 @@
     'Any COUNT(X) WHERE X is CWUser',
     ]
 
-class ComposityCopy(WebTest):
+class ComposityCopy(CubicWebTC):
 
     def test_regr_copy_view(self):
         """regression test: make sure we can ask a copy of a
@@ -34,7 +34,7 @@
         self.req.add_js('spam.js')
 
 
-class ManualWebTests(WebTest):
+class ManualCubicWebTCs(AutoPopulateTest):
     def setup_database(self):
         self.auto_populate(10)
 
@@ -59,7 +59,7 @@
 
 
 
-class ExplicitViewsTest(WebTest):
+class ExplicitViewsTest(CubicWebTC):
 
     def test_unrelateddivs(self):
         rset = self.execute('Any X WHERE X is CWUser, X login "admin"')
--- a/web/test/unittest_application.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_application.py	Fri Aug 14 00:02:08 2009 +0200
@@ -14,7 +14,7 @@
 from logilab.common.testlib import TestCase, unittest_main
 from logilab.common.decorators import clear_cache
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.fake import FakeRequest
 from cubicweb.web import Redirect, AuthenticationError, ExplicitLogin, INTERNAL_FIELD_VALUE
 from cubicweb.web.views.basecontrollers import ViewController
@@ -134,7 +134,7 @@
                            for i in (12, 13, 14)])
 
 
-class ApplicationTC(EnvBasedTC):
+class ApplicationTC(CubicWebTC):
 
     def publish(self, req, path='view'):
         return self.app.publish(path, req)
--- a/web/test/unittest_controller.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_controller.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,37 +7,35 @@
 
 from logilab.common.testlib import unittest_main
 
-from cubicweb.devtools import apptest
+from cubicweb.devtools import testlib
 
-class BaseControllerTC(apptest.ControllerTC):
+class BaseControllerTC(testlib.CubicWebTC):
 
     def test_parse_datetime_ok(self):
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24 12:18'),
-                              datetime)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24'),
-                              date)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24 12:18', 'Datetime'),
-                              datetime)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24', 'Datetime'),
-                              datetime)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24', 'Date'),
-                              date)
-        self.assertIsInstance(self.ctrl.parse_datetime('12:18', 'Time'),
-                              time)
+        ctrl = self.vreg['controllers'].select('view', self.request())
+        pd = ctrl.parse_datetime
+        self.assertIsInstance(pd('2006/06/24 12:18'), datetime)
+        self.assertIsInstance(pd('2006/06/24'), date)
+        self.assertIsInstance(pd('2006/06/24 12:18', 'Datetime'), datetime)
+        self.assertIsInstance(pd('2006/06/24', 'Datetime'), datetime)
+        self.assertIsInstance(pd('2006/06/24', 'Date'), date)
+        self.assertIsInstance(pd('12:18', 'Time'), time)
 
     def test_parse_datetime_ko(self):
+        ctrl = self.vreg['controllers'].select('view', self.request())
+        pd = ctrl.parse_datetime
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '2006/06/24 12:188', 'Datetime')
+                          pd, '2006/06/24 12:188', 'Datetime')
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '2006/06/240', 'Datetime')
+                          pd, '2006/06/240', 'Datetime')
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '2006/06/24 12:18', 'Date')
+                          pd, '2006/06/24 12:18', 'Date')
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '2006/24/06', 'Date')
+                          pd, '2006/24/06', 'Date')
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '2006/06/240', 'Date')
+                          pd, '2006/06/240', 'Date')
         self.assertRaises(ValueError,
-                          self.ctrl.parse_datetime, '12:188', 'Time')
+                          pd, '12:188', 'Time')
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_form.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_form.py	Fri Aug 14 00:02:08 2009 +0200
@@ -12,7 +12,7 @@
 from logilab.common.testlib import unittest_main, mock_object
 
 from cubicweb import Binary
-from cubicweb.devtools.testlib import WebTest
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.formfields import (IntField, StringField, RichTextField,
                                      DateTimeField, DateTimePicker,
                                      FileField, EditableFileField)
@@ -22,7 +22,7 @@
 from cubicweb.web.views.formrenderers import FormRenderer
 
 
-class FieldsFormTC(WebTest):
+class FieldsFormTC(CubicWebTC):
 
     def test_form_field_format(self):
         form = FieldsForm(self.request(), None)
@@ -32,7 +32,7 @@
         self.assertEquals(form.form_field_format(None), 'text/rest')
 
 
-class EntityFieldsFormTC(WebTest):
+class EntityFieldsFormTC(CubicWebTC):
 
     def setUp(self):
         super(EntityFieldsFormTC, self).setUp()
@@ -55,28 +55,28 @@
         self.failIf(t.eid in unrelated, unrelated)
 
     def test_form_field_vocabulary_new_entity(self):
-        e = self.etype_instance('CWUser')
-        form = EntityFieldsForm(self.request(), None, entity=e)
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
+        form = EntityFieldsForm(e.req, None, entity=e)
         unrelated = [rview for rview, reid in form.subject_relation_vocabulary('in_group')]
         # should be default groups but owners, i.e. managers, users, guests
         self.assertEquals(unrelated, [u'guests', u'managers', u'users'])
 
     def test_subject_in_state_vocabulary(self):
         # on a new entity
-        e = self.etype_instance('CWUser')
-        form = EntityFieldsForm(self.request(), None, entity=e)
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
+        form = EntityFieldsForm(e.req, None, entity=e)
         states = list(form.subject_in_state_vocabulary('in_state'))
         self.assertEquals(len(states), 1)
         self.assertEquals(states[0][0], u'activated') # list of (combobox view, state eid)
         # on an existant entity
         e = self.user()
-        form = EntityFieldsForm(self.request(), None, entity=e)
+        form = EntityFieldsForm(e.req, None, entity=e)
         states = list(form.subject_in_state_vocabulary('in_state'))
         self.assertEquals(len(states), 1)
         self.assertEquals(states[0][0], u'deactivated') # list of (combobox view, state eid)
 
     def test_consider_req_form_params(self):
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         e.eid = 'A'
         form = EntityFieldsForm(self.request(login=u'toto'), None, entity=e)
         field = StringField(name='login', eidparam=True)
@@ -86,7 +86,7 @@
 
 
     def test_linkto_field_duplication(self):
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         e.eid = 'A'
         e.req = self.req
         geid = self.execute('CWGroup X WHERE X name "users"')[0][0]
--- a/web/test/unittest_formfields.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_formfields.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,7 +11,7 @@
 from yams.constraints import StaticVocabularyConstraint, SizeConstraint
 
 from cubicweb.devtools import TestServerConfiguration
-from cubicweb.devtools.testlib import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.formwidgets import PasswordInput, TextArea, Select, Radio
 from cubicweb.web.formfields import *
 from cubicweb.web.views.forms import EntityFieldsForm
@@ -112,11 +112,11 @@
                           [(u'maybe', '1'), (u'no', '')])
 
 
-class MoreFieldsTC(EnvBasedTC):
+class MoreFieldsTC(CubicWebTC):
     def test_rtf_format_field(self):
         req = self.request()
         req.use_fckeditor = lambda: False
-        e = self.etype_instance('State')
+        e = self.vreg['etypes'].etype_class('State')(req)
         form = EntityFieldsForm(req, entity=e)
         description_field = guess_field(schema['State'], schema['description'])
         description_format_field = description_field.get_format_field(form)
--- a/web/test/unittest_magicsearch.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_magicsearch.py	Fri Aug 14 00:02:08 2009 +0200
@@ -13,21 +13,17 @@
 
 from rql import BadRQLQuery, RQLSyntaxError
 
-from cubicweb.devtools.apptest import EnvBasedTC, TestEnvironment
+from cubicweb.devtools.testlib import CubicWebTC
 
 
 translations = {
     u'CWUser' : u"Utilisateur",
-#    u'Workcase' : u"Affaire",
     u'EmailAddress' : u"Adresse",
-#    u'Division' : u"Division",
-#    u'Comment' : u"Commentaire",
     u'name' : u"nom",
     u'alias' : u"nom",
     u'surname' : u"nom",
     u'firstname' : u"prénom",
     u'state' : u"état",
-#    u'subject' : u"sujet",
     u'address' : u"adresse",
     u'use_email' : u"adel",
     }
@@ -37,12 +33,12 @@
 
 from cubicweb.web.views.magicsearch import translate_rql_tree, QSPreProcessor, QueryTranslator
 
-class QueryTranslatorTC(EnvBasedTC):
+class QueryTranslatorTC(CubicWebTC):
     """test suite for QueryTranslatorTC"""
 
     def setUp(self):
         super(QueryTranslatorTC, self).setUp()
-        self.req = self.env.create_request()
+        self.req = self.request()
         self.vreg.config.translations = {'en': _translate}
         proc = self.vreg['components'].select('magicsearch', self.req)
         self.proc = [p for p in proc.processors if isinstance(p, QueryTranslator)][0]
@@ -63,7 +59,7 @@
         self.assertEquals(rql, "Any P WHERE P is CWUser, P use_email C, P surname 'Smith'")
 
 
-class QSPreProcessorTC(EnvBasedTC):
+class QSPreProcessorTC(CubicWebTC):
     """test suite for QSPreProcessor"""
     def setUp(self):
         super(QSPreProcessorTC, self).setUp()
@@ -88,7 +84,6 @@
         eschema = self.schema.eschema('CWUser')
         self.assertEquals(translate(u'prénom', eschema), "firstname")
         self.assertEquals(translate(u'nom', eschema), 'surname')
-        #self.assert_(translate(u'nom') in ('name', 'surname'))
         eschema = self.schema.eschema('EmailAddress')
         self.assertEquals(translate(u'adresse', eschema), "address")
         self.assertEquals(translate(u'nom', eschema), 'alias')
@@ -125,7 +120,6 @@
         self.assertEquals(transform(u'adresse', 'Logi%'),
                           ('EmailAddress E WHERE E alias LIKE %(text)s', {'text': 'Logi%'}))
         self.assertRaises(BadRQLQuery, transform, "pers", "taratata")
-        #self.assertEquals(transform('CWUser', '%mi'), 'CWUser E WHERE P surname LIKE "%mi"')
 
     def test_three_words_query(self):
         """tests the 'three words shortcut queries'"""
@@ -180,7 +174,7 @@
 ## Processor Chains tests ############################################
 
 
-class ProcessorChainTC(EnvBasedTC):
+class ProcessorChainTC(CubicWebTC):
     """test suite for magic_search's processor chains"""
 
     def setUp(self):
@@ -195,7 +189,7 @@
             (u'foo',
              ("Any X WHERE X has_text %(text)s", {'text': u'foo'})),
             # XXX this sounds like a language translator test...
-            # and it fail
+            # and it fails
             (u'Utilisateur Smith',
              ('CWUser C WHERE C has_text %(text)s', {'text': u'Smith'})),
             (u'utilisateur nom Smith',
--- a/web/test/unittest_urlpublisher.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_urlpublisher.py	Fri Aug 14 00:02:08 2009 +0200
@@ -11,15 +11,14 @@
 
 from logilab.common.testlib import unittest_main
 
-from cubicweb.devtools.apptest import EnvBasedTC
-from cubicweb.devtools._apptest import FakeRequest
-
 from cubicweb.rset import ResultSet
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.devtools.fake import FakeRequest
 from cubicweb.web import NotFound, Redirect
 from cubicweb.web.views.urlrewrite import SimpleReqRewriter
 
 
-class URLPublisherTC(EnvBasedTC):
+class URLPublisherTC(CubicWebTC):
     """test suite for QSPreProcessor"""
 
     def setup_database(self):
@@ -30,7 +29,7 @@
 
     def process(self, url):
         req = self.req = self.request()
-        return self.env.app.url_resolver.process(req, url)
+        return self.app.url_resolver.process(req, url)
 
     def test_raw_path(self):
         """tests raw path resolution'"""
--- a/web/test/unittest_urlrewrite.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_urlrewrite.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,8 +7,8 @@
 """
 from logilab.common.testlib import TestCase, unittest_main
 
-from cubicweb.devtools._apptest import FakeRequest
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.devtools.fake import FakeRequest
 
 from cubicweb.web.views.urlrewrite import SimpleReqRewriter, SchemaBasedRewriter, rgx, rgx_action
 
@@ -84,7 +84,7 @@
 
 
 
-class RgxActionRewriteTC(EnvBasedTC):
+class RgxActionRewriteTC(CubicWebTC):
 
     def setup_database(self):
         self.p1 = self.create_user(u'user1')
--- a/web/test/unittest_views_actions.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_actions.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,26 +7,26 @@
 """
 from logilab.common.testlib import unittest_main
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
-class ActionsTC(EnvBasedTC):
+class ActionsTC(CubicWebTC):
     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['actions'].possible_vobjects(req, rset=rset)
-                   if action.id == 'view'][0]
+        actions = self.vreg['actions'].poss_visible_objects(req, rset=rset)
+        vaction = [action for action in actions 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['actions'].possible_vobjects(req, rset=rset)
-                         if action.id == 'sendemail'])
+        actions = self.vreg['actions'].poss_visible_objects(req, rset=rset)
+        self.failUnless([action for action in actions 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['actions'].possible_vobjects(req, rset=rset)
-                     if action.id == 'sendemail'])
+        actions = self.vreg['actions'].poss_visible_objects(req, rset=rset)
+        self.failIf([action for action in actions if action.id == 'sendemail'])
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_views_basecontrollers.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Fri Aug 14 00:02:08 2009 +0200
@@ -8,42 +8,39 @@
 import simplejson
 
 from logilab.common.testlib import unittest_main, mock_object
-from cubicweb.devtools.apptest import EnvBasedTC, ControllerTC
 
 from cubicweb import Binary, NoSelectableObject, ValidationError
 from cubicweb.view import STRICT_DOCTYPE
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.common.uilib import rql_for_eid
-
 from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError
-
 from cubicweb.entities.authobjs import CWUser
 
 
-class EditControllerTC(ControllerTC):
+class EditControllerTC(CubicWebTC):
     def setUp(self):
-        ControllerTC.setUp(self)
+        CubicWebTC.setUp(self)
         self.failUnless('users' in self.schema.eschema('CWGroup').get_groups('read'))
 
     def tearDown(self):
-        ControllerTC.tearDown(self)
+        CubicWebTC.tearDown(self)
         self.failUnless('users' in self.schema.eschema('CWGroup').get_groups('read'))
 
     def test_noparam_edit(self):
         """check behaviour of this controller without any form parameter
         """
-
-        self.req.form = {}
-        self.assertRaises(ValidationError, self.publish, self.req)
+        self.assertRaises(ValidationError, self.publish, self.request())
 
     def test_validation_unique(self):
         """test creation of two linked entities
         """
         user = self.user()
-        self.req.form = {'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': u'admin', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
+        req = self.request()
+        req.form = {'eid': 'X', '__type:X': 'CWUser',
+                    'login:X': u'admin', 'edits-login:X': u'',
+                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                    }
+        self.assertRaises(ValidationError, self.publish, req)
 
 
     def test_user_editing_itself(self):
@@ -54,7 +51,8 @@
         groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
         groups = [str(eid) for eid in groupeids]
         stateeid = [eid for eid, in self.execute('State S WHERE S name "activated"')][0]
-        self.req.form = {
+        req = self.request()
+        req.form = {
             'eid':       `user.eid`,
             '__type:'+`user.eid`:    'CWUser',
             'login:'+`user.eid`:     unicode(user.login),
@@ -69,7 +67,7 @@
             'edits-in_group:'+`user.eid`:  basegroups,
             'edits-in_state:'+`user.eid`:  `stateeid`,
             }
-        path, params = self.expect_redirect_publish()
+        path, params = self.expect_redirect_publish(req)
         e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}, 'x').get_entity(0, 0)
         self.assertEquals(e.firstname, u'Th\xe9nault')
         self.assertEquals(e.surname, u'Sylvain')
@@ -81,8 +79,6 @@
         user = self.create_user('user')
         cnx = self.login('user')
         req = self.request()
-        #self.assertEquals(self.ctrl.schema['CWUser']._groups['read'],
-        #                  ('managers', 'users'))
         req.form = {
             'eid': `user.eid`, '__type:'+`user.eid`: 'CWUser',
             '__maineid' : str(user.eid),
@@ -101,7 +97,8 @@
         """
         user = self.user()
         groupeids = [eid for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
-        self.req.form = {
+        req = self.request()
+        req.form = {
             'eid':       `user.eid`,
             '__type:'+`user.eid`:    'CWUser',
             'login:'+`user.eid`:     unicode(user.login),
@@ -112,7 +109,7 @@
             'edits-firstname:'+`user.eid`: u'',
             'edits-surname:'+`user.eid`:   u'',
             }
-        path, params = self.expect_redirect_publish()
+        path, params = self.expect_redirect_publish(req)
         e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}, 'x').get_entity(0, 0)
         self.assertEquals(e.login, user.login)
         self.assertEquals(e.firstname, u'Th\xe9nault')
@@ -124,21 +121,22 @@
 
     def test_create_multiple_linked(self):
         gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
-        self.req.form = {'eid': ['X', 'Y'],
-
-                         '__type:X': 'CWUser',
-                         '__maineid' : 'X',
-                         'login:X': u'adim', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                         'surname:X': u'Di Mascio', 'edits-surname:X': '',
+        req = self.request()
+        req.form = {'eid': ['X', 'Y'],
 
-                         'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+                    '__type:X': 'CWUser',
+                    '__maineid' : 'X',
+                    'login:X': u'adim', 'edits-login:X': u'',
+                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                    'surname:X': u'Di Mascio', 'edits-surname:X': '',
 
-                         '__type:Y': 'EmailAddress',
-                         'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
-                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
-                         }
-        path, params = self.expect_redirect_publish()
+                    'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+
+                    '__type:Y': 'EmailAddress',
+                    'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
+                    'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
+                    }
+        path, params = self.expect_redirect_publish(req)
         # should be redirected on the created person
         self.assertEquals(path, 'cwuser/adim')
         e = self.execute('Any P WHERE P surname "Di Mascio"').get_entity(0, 0)
@@ -148,17 +146,18 @@
 
     def test_edit_multiple_linked(self):
         peid = self.create_user('adim').eid
-        self.req.form = {'eid': [`peid`, 'Y'],
-                         '__type:%s'%peid: 'CWUser',
-                         'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: '',
+        req = self.request()
+        req.form = {'eid': [`peid`, 'Y'],
+                    '__type:%s'%peid: 'CWUser',
+                    'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: '',
 
-                         '__type:Y': 'EmailAddress',
-                         'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
-                         'use_email:%s'%peid: 'Y', 'edits-use_email:%s'%peid: INTERNAL_FIELD_VALUE,
+                    '__type:Y': 'EmailAddress',
+                    'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
+                    'use_email:%s'%peid: 'Y', 'edits-use_email:%s'%peid: INTERNAL_FIELD_VALUE,
 
-                         '__redirectrql': 'Any X WHERE X eid %s'%peid,
-                         }
-        path, params = self.expect_redirect_publish()
+                    '__redirectrql': 'Any X WHERE X eid %s'%peid,
+                    }
+        path, params = self.expect_redirect_publish(req)
         # should be redirected on the created person
         eid = params['rql'].split()[-1]
         e = self.execute('Any X WHERE X eid %(x)s', {'x': eid}, 'x').get_entity(0, 0)
@@ -167,7 +166,8 @@
         self.assertEquals(email.address, 'dima@logilab.fr')
 
         emaileid = email.eid
-        self.req.form = {'eid': [`peid`, `emaileid`],
+        req = self.request()
+        req.form = {'eid': [`peid`, `emaileid`],
                          '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: 'Di Masci',
                          '__type:%s'%emaileid: 'EmailAddress',
@@ -175,7 +175,7 @@
                          'use_email:%s'%peid: `emaileid`, 'edits-use_email:%s'%peid: `emaileid`,
                          '__redirectrql': 'Any X WHERE X eid %s'%peid,
                          }
-        path, params = self.expect_redirect_publish()
+        path, params = self.expect_redirect_publish(req)
         # should be redirected on the created person
         eid = params['rql'].split()[-1]
         e = self.execute('Any X WHERE X eid %(x)s', {'x': eid}, 'x').get_entity(0, 0)
@@ -188,41 +188,47 @@
         """test creation of two linked entities
         """
         user = self.user()
-        self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': u'toto', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'edits-upassword:X': u'',
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
-        self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'CWUser',
-                         'login:X': u'toto', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'',
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
+        req = self.request()
+        req.form = {'__cloned_eid:X': user.eid,
+                    'eid': 'X', '__type:X': 'CWUser',
+                    'login:X': u'toto', 'edits-login:X': u'',
+                    'upassword:X': u'toto', 'edits-upassword:X': u'',
+                    }
+        self.assertRaises(ValidationError, self.publish, req)
+        req = self.request()
+        req.form = {'__cloned_eid:X': user.eid,
+                    'eid': 'X', '__type:X': 'CWUser',
+                    'login:X': u'toto', 'edits-login:X': u'',
+                    'upassword:X': u'toto',
+                    'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'',
+                    }
+        self.assertRaises(ValidationError, self.publish, req)
 
 
     def test_interval_bound_constraint_success(self):
         feid = self.execute('INSERT File X: X name "toto.txt", X data %(data)s',
                             {'data': Binary('yo')})[0][0]
-        self.req.form = {'eid': ['X'],
-                         '__type:X': 'Salesterm',
-                         'amount:X': u'-10', 'edits-amount:X': '',
-                         'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
-        self.req.form = {'eid': ['X'],
-                         '__type:X': 'Salesterm',
-                         'amount:X': u'110', 'edits-amount:X': '',
-                         'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
-        self.req.form = {'eid': ['X'],
-                         '__type:X': 'Salesterm',
-                         'amount:X': u'10', 'edits-amount:X': '',
-                         'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
-                         }
-        self.expect_redirect_publish()
+        req = self.request()
+        req.form = {'eid': ['X'],
+                    '__type:X': 'Salesterm',
+                    'amount:X': u'-10', 'edits-amount:X': '',
+                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                }
+        self.assertRaises(ValidationError, self.publish, req)
+        req = self.request()
+        req.form = {'eid': ['X'],
+                    '__type:X': 'Salesterm',
+                    'amount:X': u'110', 'edits-amount:X': '',
+                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                    }
+        self.assertRaises(ValidationError, self.publish, req)
+        req = self.request()
+        req.form = {'eid': ['X'],
+                    '__type:X': 'Salesterm',
+                    'amount:X': u'10', 'edits-amount:X': '',
+                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                    }
+        self.expect_redirect_publish(req)
         # should be redirected on the created
         #eid = params['rql'].split()[-1]
         e = self.execute('Salesterm X').get_entity(0, 0)
@@ -232,12 +238,13 @@
         """make sure req's pending insertions are taken into account"""
         tmpgroup = self.add_entity('CWGroup', name=u"test")
         user = self.user()
-        self.req.set_session_data('pending_insert', set([(user.eid, 'in_group', tmpgroup.eid)]))
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.set_session_data('pending_insert', set([(user.eid, 'in_group', tmpgroup.eid)]))
+        path, params = self.expect_redirect_publish(req)
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
         self.assertUnorderedIterableEquals(usergroups, ['managers', 'test'])
-        self.assertEquals(self.req.get_pending_inserts(), [])
+        self.assertEquals(req.get_pending_inserts(), [])
 
 
     def test_req_pending_delete(self):
@@ -250,12 +257,13 @@
         # just make sure everything was set correctly
         self.assertUnorderedIterableEquals(usergroups, ['managers', 'test'])
         # now try to delete the relation
-        self.req.set_session_data('pending_delete', set([(user.eid, 'in_group', groupeid)]))
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.set_session_data('pending_delete', set([(user.eid, 'in_group', groupeid)]))
+        path, params = self.expect_redirect_publish(req)
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
         self.assertUnorderedIterableEquals(usergroups, ['managers'])
-        self.assertEquals(self.req.get_pending_deletes(), [])
+        self.assertEquals(req.get_pending_deletes(), [])
 
     def test_custom_attribute_handler(self):
         def custom_login_edit(self, formparams, value, relations):
@@ -265,13 +273,14 @@
         try:
             user = self.user()
             eid = repr(user.eid)
-            self.req.form = {
+            req = self.request()
+            req.form = {
                 'eid': eid,
                 '__type:'+eid:  'CWUser',
                 'login:'+eid: u'foo',
                 'edits-login:'+eid:  unicode(user.login),
                 }
-            path, params = self.expect_redirect_publish()
+            path, params = self.expect_redirect_publish(req)
             rset = self.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
             self.assertEquals(rset[0][0], 'FOO')
         finally:
@@ -279,18 +288,19 @@
 
     def test_redirect_apply_button(self):
         redirectrql = rql_for_eid(4012) # whatever
-        self.req.form = {
-                         'eid': 'A', '__type:A': 'BlogEntry',
-                         '__maineid' : 'A',
-                         'content:A': u'"13:03:43"', 'edits-content:A': '',
-                         'title:A': u'huuu', 'edits-title:A': '',
-                         '__redirectrql': redirectrql,
-                         '__redirectvid': 'primary',
-                         '__redirectparams': 'toto=tutu&tata=titi',
-                         '__form_id': 'edition',
-                         '__action_apply': '',
-                         }
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {
+            'eid': 'A', '__type:A': 'BlogEntry',
+            '__maineid' : 'A',
+            'content:A': u'"13:03:43"', 'edits-content:A': '',
+            'title:A': u'huuu', 'edits-title:A': '',
+            '__redirectrql': redirectrql,
+            '__redirectvid': 'primary',
+            '__redirectparams': 'toto=tutu&tata=titi',
+            '__form_id': 'edition',
+            '__action_apply': '',
+            }
+        path, params = self.expect_redirect_publish(req)
         self.failUnless(path.startswith('blogentry/'))
         eid = path.split('/')[1]
         self.assertEquals(params['vid'], 'edition')
@@ -301,17 +311,18 @@
 
     def test_redirect_ok_button(self):
         redirectrql = rql_for_eid(4012) # whatever
-        self.req.form = {
-                         'eid': 'A', '__type:A': 'BlogEntry',
-                         '__maineid' : 'A',
-                         'content:A': u'"13:03:43"', 'edits-content:A': '',
-                         'title:A': u'huuu', 'edits-title:A': '',
-                         '__redirectrql': redirectrql,
-                         '__redirectvid': 'primary',
-                         '__redirectparams': 'toto=tutu&tata=titi',
-                         '__form_id': 'edition',
-                         }
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {
+            'eid': 'A', '__type:A': 'BlogEntry',
+            '__maineid' : 'A',
+            'content:A': u'"13:03:43"', 'edits-content:A': '',
+            'title:A': u'huuu', 'edits-title:A': '',
+            '__redirectrql': redirectrql,
+            '__redirectvid': 'primary',
+            '__redirectparams': 'toto=tutu&tata=titi',
+            '__form_id': 'edition',
+            }
+        path, params = self.expect_redirect_publish(req)
         self.assertEquals(path, 'view')
         self.assertEquals(params['rql'], redirectrql)
         self.assertEquals(params['vid'], 'primary')
@@ -320,27 +331,30 @@
 
     def test_redirect_delete_button(self):
         eid = self.add_entity('BlogEntry', title=u'hop', content=u'hop').eid
-        self.req.form = {'eid': str(eid), '__type:%s'%eid: 'BlogEntry',
-                         '__action_delete': ''}
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {'eid': str(eid), '__type:%s'%eid: 'BlogEntry',
+                    '__action_delete': ''}
+        path, params = self.expect_redirect_publish(req)
         self.assertEquals(path, 'blogentry')
         self.assertEquals(params, {u'__message': u'entity deleted'})
         eid = self.add_entity('EmailAddress', address=u'hop@logilab.fr').eid
         self.execute('SET X use_email E WHERE E eid %(e)s, X eid %(x)s',
-                     {'x': self.session().user.eid, 'e': eid}, 'x')
+                     {'x': self.session.user.eid, 'e': eid}, 'x')
         self.commit()
-        self.req.form = {'eid': str(eid), '__type:%s'%eid: 'EmailAddress',
-                         '__action_delete': ''}
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {'eid': str(eid), '__type:%s'%eid: 'EmailAddress',
+                    '__action_delete': ''}
+        path, params = self.expect_redirect_publish(req)
         self.assertEquals(path, 'cwuser/admin')
         self.assertEquals(params, {u'__message': u'entity deleted'})
         eid1 = self.add_entity('BlogEntry', title=u'hop', content=u'hop').eid
         eid2 = self.add_entity('EmailAddress', address=u'hop@logilab.fr').eid
-        self.req.form = {'eid': [str(eid1), str(eid2)],
-                         '__type:%s'%eid1: 'BlogEntry',
-                         '__type:%s'%eid2: 'EmailAddress',
-                         '__action_delete': ''}
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {'eid': [str(eid1), str(eid2)],
+                    '__type:%s'%eid1: 'BlogEntry',
+                    '__type:%s'%eid2: 'EmailAddress',
+                    '__action_delete': ''}
+        path, params = self.expect_redirect_publish(req)
         self.assertEquals(path, 'view')
         self.assertEquals(params, {u'__message': u'entities deleted'})
 
@@ -351,23 +365,24 @@
         groups = [str(eid) for eid in groupeids]
         eeetypeeid = self.execute('CWEType X WHERE X name "CWGroup"')[0][0]
         basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
-        self.req.form = {
-                'eid':      `eeetypeeid`,
-                '__type:'+`eeetypeeid`:   'CWEType',
-                'name:'+`eeetypeeid`:     u'CWGroup',
-                'final:'+`eeetypeeid`:    False,
-                'meta:'+`eeetypeeid`:     True,
-                'description:'+`eeetypeeid`:     u'users group',
-                'read_permission:'+`eeetypeeid`:  groups,
-                #
-                'edits-name:'+`eeetypeeid`:     u'CWGroup',
-                'edits-final:'+`eeetypeeid`:    False,
-                'edits-meta:'+`eeetypeeid`:     True,
-                'edits-description:'+`eeetypeeid`:     u'users group',
-                'edits-read_permission:'+`eeetypeeid`:  basegroups,
-                }
+        req = self.request()
+        req.form = {
+            'eid':      `eeetypeeid`,
+            '__type:'+`eeetypeeid`:   'CWEType',
+            'name:'+`eeetypeeid`:     u'CWGroup',
+            'final:'+`eeetypeeid`:    False,
+            'meta:'+`eeetypeeid`:     True,
+            'description:'+`eeetypeeid`:     u'users group',
+            'read_permission:'+`eeetypeeid`:  groups,
+            #
+            'edits-name:'+`eeetypeeid`:     u'CWGroup',
+            'edits-final:'+`eeetypeeid`:    False,
+            'edits-meta:'+`eeetypeeid`:     True,
+            'edits-description:'+`eeetypeeid`:     u'users group',
+            'edits-read_permission:'+`eeetypeeid`:  basegroups,
+            }
         try:
-            path, params = self.expect_redirect_publish()
+            path, params = self.expect_redirect_publish(req)
             e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
             self.assertEquals(e.name, 'CWGroup')
             self.assertEquals([g.eid for g in e.read_permission], groupeids)
@@ -383,23 +398,24 @@
         groups = [str(eid) for eid in groupeids]
         eeetypeeid = self.execute('CWEType X WHERE X name "CWEType"')[0][0]
         basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
-        self.req.form = {
-                'eid':      `eeetypeeid`,
-                '__type:'+`eeetypeeid`:  'CWEType',
-                'name:'+`eeetypeeid`:     u'CWEType',
-                'final:'+`eeetypeeid`:    False,
-                'meta:'+`eeetypeeid`:     True,
-                'description:'+`eeetypeeid`:     u'users group',
-                'read_permission:'+`eeetypeeid`:  groups,
+        req = self.request()
+        req.form = {
+            'eid':      `eeetypeeid`,
+            '__type:'+`eeetypeeid`:  'CWEType',
+            'name:'+`eeetypeeid`:     u'CWEType',
+            'final:'+`eeetypeeid`:    False,
+            'meta:'+`eeetypeeid`:     True,
+            'description:'+`eeetypeeid`:     u'users group',
+            'read_permission:'+`eeetypeeid`:  groups,
 
-                'edits-name:'+`eeetypeeid`:     u'CWEType',
-                'edits-final:'+`eeetypeeid`:    False,
-                'edits-meta:'+`eeetypeeid`:     True,
-                'edits-description:'+`eeetypeeid`:     u'users group',
-                'edits-read_permission:'+`eeetypeeid`:  basegroups,
-                }
+            'edits-name:'+`eeetypeeid`:     u'CWEType',
+            'edits-final:'+`eeetypeeid`:    False,
+            'edits-meta:'+`eeetypeeid`:     True,
+            'edits-description:'+`eeetypeeid`:     u'users group',
+            'edits-read_permission:'+`eeetypeeid`:  basegroups,
+            }
         try:
-            path, params = self.expect_redirect_publish()
+            path, params = self.expect_redirect_publish(req)
             e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
             self.assertEquals(e.name, 'CWEType')
             self.assertEquals(sorted(g.eid for g in e.read_permission), groupeids)
@@ -413,12 +429,13 @@
 
         this seems to be postgres (tsearch?) specific
         """
-        self.req.form = {
-                         'eid': 'A', '__type:A': 'BlogEntry',
-                         '__maineid' : 'A',
-                         'title:A': u'"13:03:40"', 'edits-title:A': '',
-                         'content:A': u'"13:03:43"', 'edits-content:A': ''}
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {
+            'eid': 'A', '__type:A': 'BlogEntry',
+            '__maineid' : 'A',
+            'title:A': u'"13:03:40"', 'edits-title:A': '',
+            'content:A': u'"13:03:43"', 'edits-content:A': ''}
+        path, params = self.expect_redirect_publish(req)
         self.failUnless(path.startswith('blogentry/'))
         eid = path.split('/')[1]
         e = self.execute('Any C, T WHERE C eid %(x)s, C content T', {'x': eid}, 'x').get_entity(0, 0)
@@ -428,29 +445,31 @@
 
     def test_nonregr_multiple_empty_email_addr(self):
         gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
-        self.req.form = {'eid': ['X', 'Y'],
-
-                         '__type:X': 'CWUser',
-                         'login:X': u'adim', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                         'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+        req = self.request()
+        req.form = {'eid': ['X', 'Y'],
 
-                         '__type:Y': 'EmailAddress',
-                         'address:Y': u'', 'edits-address:Y': '',
-                         'alias:Y': u'', 'edits-alias:Y': '',
-                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
-                         }
-        self.assertRaises(ValidationError, self.publish, self.req)
+                    '__type:X': 'CWUser',
+                    'login:X': u'adim', 'edits-login:X': u'',
+                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                    'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+
+                    '__type:Y': 'EmailAddress',
+                    'address:Y': u'', 'edits-address:Y': '',
+                    'alias:Y': u'', 'edits-alias:Y': '',
+                    'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
+                    }
+        self.assertRaises(ValidationError, self.publish, req)
 
     def test_nonregr_copy(self):
         user = self.user()
-        self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'CWUser',
-                         '__maineid' : 'X',
-                         'login:X': u'toto', 'edits-login:X': u'',
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                         }
-        path, params = self.expect_redirect_publish()
+        req = self.request()
+        req.form = {'__cloned_eid:X': user.eid,
+                    'eid': 'X', '__type:X': 'CWUser',
+                    '__maineid' : 'X',
+                    'login:X': u'toto', 'edits-login:X': u'',
+                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                    }
+        path, params = self.expect_redirect_publish(req)
         self.assertEquals(path, 'cwuser/toto')
         e = self.execute('Any X WHERE X is CWUser, X login "toto"').get_entity(0, 0)
         self.assertEquals(e.login, 'toto')
@@ -466,29 +485,30 @@
             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})
-            self.req.form = {'__cloned_eid:X': p.eid,
-                             'eid': 'X', '__type:X': 'CWUser',
-                             'login': u'dodo', 'edits-login': u'dodo',
-                             'surname:X': u'Boom', 'edits-surname:X': u'',
-                             '__errorurl' : "whatever but required",
+            req = self.request()
+            req.form = {'__cloned_eid:X': p.eid,
+                        'eid': 'X', '__type:X': 'CWUser',
+                        'login': u'dodo', 'edits-login': u'dodo',
+                        'surname:X': u'Boom', 'edits-surname:X': u'',
+                        '__errorurl' : "whatever but required",
                              }
             # try to emulate what really happens in the web application
             # 1/ validate form => EditController.publish raises a ValidationError
             #    which fires a Redirect
             # 2/ When re-publishing the copy form, the publisher implicitly commits
             try:
-                self.env.app.publish('edit', self.req)
+                self.app.publish('edit', req)
             except Redirect:
-                self.req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
-                self.req.form['vid'] = 'copy'
-                self.env.app.publish('view', self.req)
+                req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
+                req.form['vid'] = 'copy'
+                self.app.publish('view', req)
             rset = self.execute('CWUser P WHERE P surname "Boom"')
             self.assertEquals(len(rset), 0)
         finally:
             p.__class__.skip_copy_for = old_skips
 
 
-class EmbedControllerTC(EnvBasedTC):
+class EmbedControllerTC(CubicWebTC):
 
     def test_nonregr_embed_publish(self):
         # This test looks a bit stupid but at least it will probably
@@ -500,23 +520,23 @@
         result = controller.publish(rset=None)
 
 
-class ReportBugControllerTC(EnvBasedTC):
+class ReportBugControllerTC(CubicWebTC):
 
     def test_usable_by_guets(self):
-        req = self.request()
-        self.vreg['controllers'].select('reportbug', req)
+        self.login('anon')
+        self.vreg['controllers'].select('reportbug', self.request())
 
 
-class SendMailControllerTC(EnvBasedTC):
+class SendMailControllerTC(CubicWebTC):
 
     def test_not_usable_by_guets(self):
         self.login('anon')
-        req = self.request()
-        self.assertRaises(NoSelectableObject, self.env.vreg['controllers'].select, 'sendmail', req)
+        self.assertRaises(NoSelectableObject,
+                          self.vreg['controllers'].select, 'sendmail', self.request())
 
 
 
-class JSONControllerTC(EnvBasedTC):
+class JSONControllerTC(CubicWebTC):
 
     def ctrl(self, req=None):
         req = req or self.request(url='http://whatever.fr/')
--- a/web/test/unittest_views_basetemplates.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_basetemplates.py	Fri Aug 14 00:02:08 2009 +0200
@@ -5,11 +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 cubicweb.devtools.testlib import WebTest
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.htmlparser import DTDValidator
 
 
-class LogFormTemplateTC(WebTest):
+class LogFormTemplateTC(CubicWebTC):
 
     def _login_labels(self):
         valid = self.content_type_validators.get('text/html', DTDValidator)()
--- a/web/test/unittest_views_baseviews.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_baseviews.py	Fri Aug 14 00:02:08 2009 +0200
@@ -10,7 +10,7 @@
 from logilab.common.testlib import unittest_main
 from logilab.mtconverter import html_unescape
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.web.htmlwidgets import TableWidget
 from cubicweb.web.views import vid_from_rset
@@ -18,7 +18,7 @@
 def loadjson(value):
     return loads(html_unescape(value))
 
-class VidFromRsetTC(EnvBasedTC):
+class VidFromRsetTC(CubicWebTC):
 
     def test_no_rset(self):
         req = self.request()
@@ -84,7 +84,7 @@
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'table')
 
 
-class TableViewTC(EnvBasedTC):
+class TableViewTC(CubicWebTC):
 
     def _prepare_entity(self):
         e = self.add_entity("State", name=u'<toto>', description=u'loo"ong blabla')
--- a/web/test/unittest_views_editforms.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_editforms.py	Fri Aug 14 00:02:08 2009 +0200
@@ -6,14 +6,13 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
-from cubicweb.devtools.testlib import WebTest
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.views.autoform import AutomaticEntityForm as AEF
 from cubicweb.web.formwidgets import AutoCompletionWidget
 def rbc(entity, category):
     return [(rschema.type, x) for rschema, tschemas, x in AEF.erelations_by_category(entity, category)]
 
-class AutomaticEntityFormTC(EnvBasedTC):
+class AutomaticEntityFormTC(CubicWebTC):
 
     def test_custom_widget(self):
         AEF.rfields_kwargs.tag_subject_of(('CWUser', 'login', '*'),
@@ -29,7 +28,7 @@
         #for (rtype, role, stype, otype), tag in AEF.rcategories._tagdefs.items():
         #    if rtype == 'tags':
         #        print rtype, role, stype, otype, ':', tag
-        e = self.etype_instance('CWUser')
+        e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         # see custom configuration in views.cwuser
         self.assertEquals(rbc(e, 'primary'),
                           [('login', 'subject'),
@@ -76,7 +75,7 @@
         self.failIf(AEF.rinlined.etype_get('CWUser', 'primary_email', 'subject'))
 
     def test_personne_relations_by_category(self):
-        e = self.etype_instance('Personne')
+        e = self.vreg['etypes'].etype_class('Personne')(self.request())
         self.assertListEquals(rbc(e, 'primary'),
                               [('nom', 'subject'),
                                ('eid', 'subject')
@@ -124,7 +123,7 @@
         self.failIf(any(f for f in form.fields if f is None))
 
 
-class FormViewsTC(WebTest):
+class FormViewsTC(CubicWebTC):
     def test_delete_conf_formview(self):
         rset = self.execute('CWGroup X')
         self.view('deleteconf', rset, template=None).source
--- a/web/test/unittest_views_navigation.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_navigation.py	Fri Aug 14 00:02:08 2009 +0200
@@ -7,14 +7,14 @@
 """
 
 from logilab.common.testlib import unittest_main, mock_object
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
 from cubicweb.web.views.navigation import PageNavigation, SortedNavigation
 from cubicweb.web.views.ibreadcrumbs import BreadCrumbEntityVComponent
 
 BreadCrumbEntityVComponent.visible = True
 
-class NavigationTC(EnvBasedTC):
+class NavigationTC(CubicWebTC):
 
     def test_navigation_selection_whatever(self):
         req = self.request()
@@ -40,10 +40,10 @@
     def test_navigation_selection_not_enough(self):
         req = self.request()
         rset = self.execute('Any X,N LIMIT 10 WHERE X name N')
-        navcomp = self.vreg['components'].select_object('navigation', req, rset=rset)
+        navcomp = self.vreg['components'].select_or_none('navigation', req, rset=rset)
         self.assertEquals(navcomp, None)
         req.set_search_state('W:X:Y:Z')
-        navcomp = self.vreg['components'].select_object('navigation', req, rset=rset)
+        navcomp = self.vreg['components'].select_or_none('navigation', req, rset=rset)
         self.assertEquals(navcomp, None)
         req.set_search_state('normal')
 
@@ -96,18 +96,18 @@
 
 
 
-class ContentNavigationTC(EnvBasedTC):
+class ContentNavigationTC(CubicWebTC):
 
     def test_component_context(self):
         view = mock_object(is_primary=lambda x: True)
         rset = self.execute('CWUser X LIMIT 1')
         req = self.request()
-        objs = self.vreg['contentnavigation'].possible_vobjects(
+        objs = self.vreg['contentnavigation'].poss_visible_objects(
             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['contentnavigation'].possible_vobjects(
+        objs = self.vreg['contentnavigation'].poss_visible_objects(
             req, rset=rset, view=view, context='navbottom')
         # breadcrumbs should _NOT_ be in footers by default
         clsids = set(obj.id for obj in objs)
@@ -116,12 +116,12 @@
                      'P value "navbottom"')
         # breadcrumbs should now be in footers
         req.cnx.commit()
-        objs = self.vreg['contentnavigation'].possible_vobjects(
+        objs = self.vreg['contentnavigation'].poss_visible_objects(
             req, rset=rset, view=view, context='navbottom')
 
         clsids = [obj.id for obj in objs]
         self.failUnless('breadcrumbs' in clsids)
-        objs = self.vreg['contentnavigation'].possible_vobjects(
+        objs = self.vreg['contentnavigation'].poss_visible_objects(
             req, rset=rset, view=view, context='navtop')
 
         clsids = [obj.id for obj in objs]
--- a/web/test/unittest_views_pyviews.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_pyviews.py	Fri Aug 14 00:02:08 2009 +0200
@@ -1,7 +1,7 @@
 from logilab.common.testlib import unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 
-class PyViewsTC(EnvBasedTC):
+class PyViewsTC(CubicWebTC):
 
     def test_pyvaltable(self):
         content = self.vreg['views'].render('pyvaltable', self.request(),
--- a/web/test/unittest_views_searchrestriction.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_views_searchrestriction.py	Fri Aug 14 00:02:08 2009 +0200
@@ -5,11 +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 cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.facet import insert_attr_select_relation, prepare_facets_rqlst
 
 
-class InsertAttrRelationTC(EnvBasedTC):
+class InsertAttrRelationTC(CubicWebTC):
 
     def parse(self, query):
         rqlst = self.vreg.parse(self.session, query)
--- a/web/test/unittest_viewselector.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_viewselector.py	Fri Aug 14 00:02:08 2009 +0200
@@ -4,7 +4,7 @@
 """
 from logilab.common.testlib import unittest_main
 
-from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb import CW_SOFTWARE_ROOT as BASE, Binary
 from cubicweb.selectors import (match_user_groups, implements,
                                 specified_etype_implements, rql_condition,
@@ -26,7 +26,7 @@
                ('siteinfo', actions.SiteInfoAction),
                ]
 
-class ViewSelectorTC(EnvBasedTC):
+class ViewSelectorTC(CubicWebTC):
 
     def setup_database(self):
         self.add_entity('BlogEntry', title=u"une news !", content=u"cubicweb c'est beau")
@@ -79,12 +79,12 @@
                               ('systempropertiesform', cwproperties.SystemCWPropertiesForm)])
 
     def test_possible_views_noresult(self):
-        rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
+        rset, req = self.rset_and_req('Any X WHERE X eid 999999')
         self.assertListEqual(self.pviews(req, rset),
                              [])
 
     def test_possible_views_one_egroup(self):
-        rset, req = self.env.get_rset_and_req('CWGroup X WHERE X name "managers"')
+        rset, req = self.rset_and_req('CWGroup X WHERE X name "managers"')
         self.assertListEqual(self.pviews(req, rset),
                              [('adaptedlist', baseviews.AdaptedListView),
                               ('csvexport', csvexport.CSVRsetView),
@@ -107,7 +107,7 @@
                               ])
 
     def test_possible_views_multiple_egroups(self):
-        rset, req = self.env.get_rset_and_req('CWGroup X')
+        rset, req = self.rset_and_req('CWGroup X')
         self.assertListEqual(self.pviews(req, rset),
                              [('adaptedlist', baseviews.AdaptedListView),
                               ('csvexport', csvexport.CSVRsetView),
@@ -131,16 +131,16 @@
 
     def test_propertiesform_admin(self):
         assert self.vreg['views']['propertiesform']
-        rset1, req1 = self.env.get_rset_and_req('CWUser X WHERE X login "admin"')
-        rset2, req2 = self.env.get_rset_and_req('CWUser X WHERE X login "anon"')
+        rset1, req1 = self.rset_and_req('CWUser X WHERE X login "admin"')
+        rset2, req2 = self.rset_and_req('CWUser X WHERE X login "anon"')
         self.failUnless(self.vreg['views'].select('propertiesform', req1, rset=None))
         self.failUnless(self.vreg['views'].select('propertiesform', req1, rset=rset1))
         self.failUnless(self.vreg['views'].select('propertiesform', req2, rset=rset2))
 
     def test_propertiesform_anon(self):
         self.login('anon')
-        rset1, req1 = self.env.get_rset_and_req('CWUser X WHERE X login "admin"')
-        rset2, req2 = self.env.get_rset_and_req('CWUser X WHERE X login "anon"')
+        rset1, req1 = self.rset_and_req('CWUser X WHERE X login "admin"')
+        rset2, req2 = self.rset_and_req('CWUser X WHERE X login "anon"')
         self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req1, rset=None)
         self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req1, rset=rset1)
         self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req1, rset=rset2)
@@ -148,14 +148,14 @@
     def test_propertiesform_jdoe(self):
         self.create_user('jdoe')
         self.login('jdoe')
-        rset1, req1 = self.env.get_rset_and_req('CWUser X WHERE X login "admin"')
-        rset2, req2 = self.env.get_rset_and_req('CWUser X WHERE X login "jdoe"')
+        rset1, req1 = self.rset_and_req('CWUser X WHERE X login "admin"')
+        rset2, req2 = self.rset_and_req('CWUser X WHERE X login "jdoe"')
         self.failUnless(self.vreg['views'].select('propertiesform', req1, rset=None))
         self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'propertiesform', req1, rset=rset1)
         self.failUnless(self.vreg['views'].select('propertiesform', req2, rset=rset2))
 
     def test_possible_views_multiple_different_types(self):
-        rset, req = self.env.get_rset_and_req('Any X')
+        rset, req = self.rset_and_req('Any X')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', csvexport.CSVRsetView),
                               ('ecsvexport', csvexport.CSVEntityView),
@@ -177,7 +177,7 @@
                               ])
 
     def test_possible_views_any_rset(self):
-        rset, req = self.env.get_rset_and_req('Any N, X WHERE X in_group Y, Y name N')
+        rset, req = self.rset_and_req('Any N, X WHERE X in_group Y, Y name N')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', csvexport.CSVRsetView),
                               ('editable-table', tableview.EditableTableView),
@@ -186,7 +186,7 @@
                               ])
 
     def test_possible_views_multiple_eusers(self):
-        rset, req = self.env.get_rset_and_req('CWUser X')
+        rset, req = self.rset_and_req('CWUser X')
         self.assertListEqual(self.pviews(req, rset),
                              [('adaptedlist', baseviews.AdaptedListView),
                               ('csvexport', csvexport.CSVRsetView),
@@ -219,14 +219,14 @@
 
                               })
     def test_possible_actions_no_entity(self):
-        rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
+        rset, req = self.rset_and_req('Any X WHERE X eid 999999')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
                               })
 
     def test_possible_actions_same_type_entities(self):
-        rset, req = self.env.get_rset_and_req('CWGroup X')
+        rset, req = self.rset_and_req('CWGroup X')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
@@ -236,7 +236,7 @@
                               })
 
     def test_possible_actions_different_types_entities(self):
-        rset, req = self.env.get_rset_and_req('Any X')
+        rset, req = self.rset_and_req('Any X')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
@@ -244,13 +244,13 @@
                               })
 
     def test_possible_actions_final_entities(self):
-        rset, req = self.env.get_rset_and_req('Any N, X WHERE X in_group Y, Y name N')
+        rset, req = self.rset_and_req('Any N, X WHERE X in_group Y, Y name N')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS})
 
     def test_possible_actions_eetype_cwuser_entity(self):
-        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWUser"')
+        rset, req = self.rset_and_req('CWEType X WHERE X name "CWUser"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
@@ -292,7 +292,7 @@
                              self.vreg['views'].select, 'table', req, rset=rset)
 
         # no entity
-        rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
+        rset, req = self.rset_and_req('Any X WHERE X eid 999999')
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg['views'].select, 'index', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
@@ -302,7 +302,7 @@
         self.failUnlessRaises(NoSelectableObject,
                              self.vreg['views'].select, 'table', req, rset=rset)
         # one entity
-        rset, req = self.env.get_rset_and_req('CWGroup X WHERE X name "managers"')
+        rset, req = self.rset_and_req('CWGroup X WHERE X name "managers"')
         self.assertIsInstance(self.vreg['views'].select('primary', req, rset=rset),
                              primary.PrimaryView)
         self.assertIsInstance(self.vreg['views'].select('list', req, rset=rset),
@@ -316,7 +316,7 @@
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg['views'].select, 'index', req, rset=rset)
         # list of entities of the same type
-        rset, req = self.env.get_rset_and_req('CWGroup X')
+        rset, req = self.rset_and_req('CWGroup X')
         self.assertIsInstance(self.vreg['views'].select('primary', req, rset=rset),
                              primary.PrimaryView)
         self.assertIsInstance(self.vreg['views'].select('list', req, rset=rset),
@@ -326,7 +326,7 @@
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg['views'].select, 'creation', req, rset=rset)
         # list of entities of different types
-        rset, req = self.env.get_rset_and_req('Any X')
+        rset, req = self.rset_and_req('Any X')
         self.assertIsInstance(self.vreg['views'].select('primary', req, rset=rset),
                                   primary.PrimaryView)
         self.assertIsInstance(self.vreg['views'].select('list', req, rset=rset),
@@ -338,7 +338,7 @@
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg['views'].select, 'index', req, rset=rset)
         # whatever
-        rset, req = self.env.get_rset_and_req('Any N, X WHERE X in_group Y, Y name N')
+        rset, req = self.rset_and_req('Any N, X WHERE X in_group Y, Y name N')
         self.assertIsInstance(self.vreg['views'].select('table', req, rset=rset),
                                   tableview.TableView)
         self.failUnlessRaises(NoSelectableObject,
@@ -352,7 +352,7 @@
         self.failUnlessRaises(NoSelectableObject,
                              self.vreg['views'].select, 'edition', req, rset=rset)
         # mixed query
-        rset, req = self.env.get_rset_and_req('Any U,G WHERE U is CWUser, G is CWGroup')
+        rset, req = self.rset_and_req('Any U,G WHERE U is CWUser, G is CWGroup')
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg['views'].select, 'edition', req, rset=rset)
         self.failUnlessRaises(NoSelectableObject,
@@ -363,7 +363,7 @@
     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"')
+        rset, req = self.rset_and_req('Image X WHERE X name "bim.png"')
         self.assertIsInstance(self.vreg['views'].select('primary', req, rset=rset),
                               idownloadable.IDownloadablePrimaryView)
 
@@ -371,12 +371,12 @@
     def test_score_entity_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"')
+        rset, req = self.rset_and_req('Image X WHERE X name "bim.png"')
         self.assertIsInstance(self.vreg['views'].select('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"')
+        rset, req = self.rset_and_req('File X WHERE X name "bim.txt"')
         self.assertRaises(NoSelectableObject, self.vreg['views'].select, 'image', req, rset=rset)
 
 
@@ -386,7 +386,7 @@
             rset = None
             req = self.request()
         else:
-            rset, req = self.env.get_rset_and_req(rql)
+            rset, req = self.rset_and_req(rql)
         try:
             self.vreg['views'].render(vid, req, rset=rset, **args)
         except:
@@ -439,7 +439,7 @@
         del self.vreg['actions']['testaction']
 
     def test(self):
-        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWEType"')
+        rset, req = self.rset_and_req('CWEType X WHERE X name "CWEType"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
@@ -450,7 +450,7 @@
                                               ('testaction', CWETypeRQLAction),
                                               ],
                               })
-        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWRType"')
+        rset, req = self.rset_and_req('CWEType X WHERE X name "CWRType"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
--- a/web/test/unittest_webconfig.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/test/unittest_webconfig.py	Fri Aug 14 00:02:08 2009 +0200
@@ -9,8 +9,7 @@
 
 from logilab.common.testlib import TestCase, unittest_main
 
-from cubicweb.devtools._apptest import FakeRequest
-from cubicweb.devtools import ApptestConfiguration
+from cubicweb.devtools import ApptestConfiguration, fake
 
 class WebconfigTC(TestCase):
     def setUp(self):
@@ -21,7 +20,7 @@
     def test_nonregr_print_css_as_list(self):
         """make sure PRINT_CSS *must* is a list"""
         config = self.config
-        req = FakeRequest()
+        req = fake.FakeRequest()
         print_css = req.external_resource('STYLESHEETS_PRINT')
         self.failUnless(isinstance(print_css, list))
         ie_css = req.external_resource('IE_STYLESHEETS')
--- a/web/uicfg.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/uicfg.py	Fri Aug 14 00:02:08 2009 +0200
@@ -69,7 +69,7 @@
 
 from cubicweb import neg_role, onevent
 from cubicweb.rtags import (RelationTags, RelationTagsBool,
-                            RelationTagsSet, RelationTagsDict)
+                            RelationTagsSet, RelationTagsDict, register_rtag)
 from cubicweb.web import formwidgets
 
 
@@ -151,12 +151,25 @@
 # * 'schema'
 # * 'subobject' (not displayed by default)
 
-indexview_etype_section = {'EmailAddress': 'subobject',
-                           'CWUser': 'system',
-                           'CWGroup': 'system',
-                           'CWPermission': 'system',
-                           }
+class InitializableDict(dict):
+    def __init__(self, *args, **kwargs):
+        super(InitializableDict, self).__init__(*args, **kwargs)
+        register_rtag(self)
 
+    def init(self, schema, check=True):
+        for eschema in schema.entities():
+            if eschema.schema_entity():
+                self.setdefault(eschema, 'schema')
+            elif eschema.is_subobject(strict=True):
+                self.setdefault(eschema, 'subobject')
+            else:
+                self.setdefault(eschema, 'application')
+
+indexview_etype_section = InitializableDict(EmailAddress='subobject',
+                                            CWUser='system',
+                                            CWGroup='system',
+                                            CWPermission='system',
+                                            )
 
 # autoform.AutomaticEntityForm configuration ##################################
 
--- a/web/views/actions.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/actions.py	Fri Aug 14 00:02:08 2009 +0200
@@ -158,14 +158,13 @@
     order = 15
 
     @classmethod
-    def registered(cls, vreg):
-        super(ManagePermissionsAction, cls).registered(vreg)
+    def __registered__(cls, vreg):
         if 'require_permission' in vreg.schema:
             cls.__select__ = (one_line_rset() & non_final_entity() &
                               (match_user_groups('managers')
                                | relation_possible('require_permission', 'subject', 'CWPermission',
                                                    action='add')))
-        return super(ManagePermissionsAction, cls).registered(vreg)
+        return super(ManagePermissionsAction, cls).__registered__(vreg)
 
     def url(self):
         return self.rset.get_entity(self.row or 0, self.col or 0).absolute_url(vid='security')
--- a/web/views/ajaxedit.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/ajaxedit.py	Fri Aug 14 00:02:08 2009 +0200
@@ -21,7 +21,7 @@
     __registry__ = 'views'
     __select__ = (match_form_params('rtype', 'target')
                   | match_kwargs('rtype', 'target'))
-    property_defs = {} # don't want to inherit this from Box
+    cw_property_defs = {} # don't want to inherit this from Box
     id = 'xaddrelation'
     expected_kwargs = form_params = ('rtype', 'target')
 
@@ -31,7 +31,7 @@
         self.rtype = rtype or self.req.form['rtype']
         self.target = target or self.req.form['target']
         self.etype = etype or self.req.form.get('etype')
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         rschema = self.schema.rschema(self.rtype)
         if not self.etype:
             if self.target == 'object':
--- a/web/views/basecomponents.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/basecomponents.py	Fri Aug 14 00:02:08 2009 +0200
@@ -29,7 +29,7 @@
 class RQLInputForm(component.Component):
     """build the rql input form, usually displayed in the header"""
     id = 'rqlinput'
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     visible = False
 
     def call(self, view=None):
@@ -47,7 +47,7 @@
 <input type="text" id="rql" name="rql" value="%s"  title="%s" tabindex="%s" accesskey="q" class="searchField" />
 <input type="submit" value="" class="rqlsubmit" tabindex="%s" />
 </fieldset>
-''' % (not self.propval('visible') and 'hidden' or '',
+''' % (not self.cw_propval('visible') and 'hidden' or '',
        self.build_url('view'), xml_escape(rql), req._('full text or RQL query'), req.next_tabindex(),
         req.next_tabindex()))
         if self.req.search_state[0] != 'normal':
@@ -59,7 +59,7 @@
 class ApplLogo(component.Component):
     """build the instance logo, usually displayed in the header"""
     id = 'logo'
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
     site_wide = True
 
@@ -71,7 +71,7 @@
 class ApplHelp(component.Component):
     """build the help button, usually displayed in the header"""
     id = 'help'
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     def call(self):
         self.w(u'<a href="%s" class="help" title="%s">&nbsp;</a>'
                % (self.build_url(_restpath='doc/main'),
@@ -82,7 +82,7 @@
     """if the user is the anonymous user, build a link to login
     else a link to the connected user object with a loggout link
     """
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
     site_wide = True
     id = 'loggeduserlink'
@@ -124,7 +124,7 @@
     __select__ = yes()
     id = 'applmessages'
     # don't want user to hide this component using an cwproperty
-    property_defs = {}
+    cw_property_defs = {}
 
     def call(self):
         msgs = [msg for msg in (self.req.get_shared_data('sources_error', pop=True),
@@ -140,7 +140,7 @@
 class ApplicationName(component.Component):
     """display the instance name"""
     id = 'appliname'
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
     site_wide = True
 
@@ -170,7 +170,7 @@
     id = 'etypenavigation'
     __select__ = two_etypes_rset() | match_form_params('__restrtype', '__restrtypes',
                                                        '__restrrql')
-    property_defs = VISIBLE_PROP_DEF
+    cw_property_defs = VISIBLE_PROP_DEF
     # don't want user to hide this component using an cwproperty
     site_wide = True
     visible = False # disabled by default
--- a/web/views/basecontrollers.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/basecontrollers.py	Fri Aug 14 00:02:08 2009 +0200
@@ -290,7 +290,7 @@
     def _exec(self, rql, args=None, eidkey=None, rocheck=True):
         """json mode: execute RQL and return resultset as json"""
         if rocheck:
-            self.ensure_ro_rql(rql)
+            self.req.ensure_ro_rql(rql)
         try:
             return self.req.execute(rql, args, eidkey)
         except Exception, ex:
--- a/web/views/baseforms.py	Fri Aug 14 00:01:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,607 +0,0 @@
-"""Set of HTML automatic forms to create, delete, copy or edit a single entity
-or a list of entities of the same type
-
-: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"
-
-from copy import copy
-
-from simplejson import dumps
-
-from logilab.mtconverter import xml_escape
-from logilab.common.decorators import cached
-
-from cubicweb.selectors import (specified_etype_implements, accepts_etype_compat,
-                                non_final_entity, match_kwargs, one_line_rset)
-from cubicweb.view import View, EntityView
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
-from cubicweb.web.controller import NAV_FORM_PARAMETERS
-from cubicweb.web.widgets import checkbox, InputWidget, ComboBoxWidget
-from cubicweb.web.form import FormMixIn
-from cubicweb.web.views.autoform import AutomaticEntityForm
-
-_ = unicode
-
-
-class EditionForm(FormMixIn, EntityView):
-    """primary entity edition form
-
-    When generating a new attribute_input, the editor will look for a method
-    named 'default_ATTRNAME' on the entity instance, where ATTRNAME is the
-    name of the attribute being edited. You may use this feature to compute
-    dynamic default values such as the 'tomorrow' date or the user's login
-    being connected
-    """
-    id = 'edition'
-    __select__ = one_line_rset() & non_final_entity()
-
-    title = _('edition')
-    controller = 'edit'
-    skip_relations = set()
-
-    EDITION_BODY = u'''\
- %(errormsg)s
-<form id="%(formid)s" class="entityForm" cubicweb:target="eformframe"
-      method="post" onsubmit="%(onsubmit)s" enctype="%(enctype)s" action="%(action)s">
- %(title)s
- <div id="progress">%(inprogress)s</div>
- <div class="iformTitle"><span>%(mainattrs_label)s</span></div>
- <div class="formBody"><fieldset>
- %(base)s
- %(attrform)s
- %(relattrform)s
-</fieldset>
- %(relform)s
- </div>
- <table width="100%%">
-  <tbody>
-   <tr><td align="center">
-     %(validate)s
-   </td><td style="align: right; width: 50%%;">
-     %(apply)s
-     %(cancel)s
-   </td></tr>
-  </tbody>
- </table>
-</form>
-'''
-
-    def cell_call(self, row, col, **kwargs):
-        self.req.add_js( ('cubicweb.ajax.js', ) )
-        entity = self.complete_entity(row, col)
-        self.edit_form(entity, kwargs)
-
-    def edit_form(self, entity, kwargs):
-        varmaker = self.req.get_page_data('rql_varmaker')
-        if varmaker is None:
-            varmaker = self.req.varmaker
-            self.req.set_page_data('rql_varmaker', varmaker)
-        self.varmaker = varmaker
-        self.w(self.EDITION_BODY % self.form_context(entity, kwargs))
-
-    def form_context(self, entity, kwargs):
-        """returns the dictionnary used to fill the EDITION_BODY template
-
-        If you create your own edition form, you can probably just override
-        `EDITION_BODY` and `form_context`
-        """
-        if self.need_multipart(entity):
-            enctype = 'multipart/form-data'
-        else:
-            enctype = 'application/x-www-form-urlencoded'
-        self._hiddens = []
-        if entity.eid is None:
-            entity.eid = self.varmaker.next()
-        # XXX (hack) action_title might need __linkto req's original value
-        #            and widgets such as DynamicComboWidget might change it
-        #            so we need to compute title before calling atttributes_form
-        formtitle = self.action_title(entity)
-        # be sure to call .*_form first so tabindexes are correct and inlined
-        # fields errors are consumed
-        if not entity.has_eid() or entity.has_perm('update'):
-            attrform = self.attributes_form(entity, kwargs)
-        else:
-            attrform = ''
-        inlineform = self.inline_entities_form(entity, kwargs)
-        relform = self.relations_form(entity, kwargs)
-        vindex = self.req.next_tabindex()
-        aindex = self.req.next_tabindex()
-        cindex = self.req.next_tabindex()
-        self.add_hidden_web_behaviour_params(entity)
-        _ = self.req._
-        return {
-            'formid'   : self.domid,
-            'onsubmit' : self.on_submit(entity),
-            'enctype'  : enctype,
-            'errormsg' : self.error_message(),
-            'action'   : self.build_url('validateform'),
-            'eids'     : entity.has_eid() and [entity.eid] or [],
-            'inprogress': _('validating...'),
-            'title'    : formtitle,
-            'mainattrs_label' : _('main informations'),
-            'reseturl' : self.redirect_url(entity),
-            'attrform' : attrform,
-            'relform'  : relform,
-            'relattrform': inlineform,
-            'base'     : self.base_form(entity, kwargs),
-            'validate' : self.button_ok(tabindex=vindex),
-            'apply'    : self.button_apply(tabindex=aindex),
-            'cancel'   : self.button_cancel(tabindex=cindex),
-            }
-
-    @property
-    def formid(self):
-        return self.id
-
-    def action_title(self, entity):
-        """form's title"""
-        ptitle = self.req._(self.title)
-        return u'<div class="formTitle"><span>%s %s</span></div>' % (
-            entity.dc_type(), ptitle and '(%s)' % ptitle)
-
-
-    def base_form(self, entity, kwargs):
-        output = []
-        for name, value, iid in self._hiddens:
-            if isinstance(value, basestring):
-                value = xml_escape(value)
-            if iid:
-                output.append(u'<input id="%s" type="hidden" name="%s" value="%s" />'
-                              % (iid, name, value))
-            else:
-                output.append(u'<input type="hidden" name="%s" value="%s" />'
-                              % (name, value))
-        return u'\n'.join(output)
-
-    def add_hidden_web_behaviour_params(self, entity):
-        """inserts hidden params controlling how errors and redirection
-        should be handled
-        """
-        req = self.req
-        self._hiddens.append( (u'__maineid', entity.eid, u'') )
-        self._hiddens.append( (u'__errorurl', req.url(), u'errorurl') )
-        self._hiddens.append( (u'__form_id', self.formid, u'') )
-        for param in NAV_FORM_PARAMETERS:
-            value = req.form.get(param)
-            if value:
-                self._hiddens.append( (param, value, u'') )
-        msg = self.submited_message()
-        # If we need to directly attach the new object to another one
-        for linkto in req.list_form_param('__linkto'):
-            self._hiddens.append( ('__linkto', linkto, '') )
-            msg = '%s %s' % (msg, self.req._('and linked'))
-        self._hiddens.append( ('__message', msg, '') )
-
-
-    def attributes_form(self, entity, kwargs, include_eid=True):
-        """create a form to edit entity's attributes"""
-        html = []
-        w = html.append
-        eid = entity.eid
-        wdg = entity.get_widget
-        lines = (wdg(rschema, x) for rschema, x in self.editable_attributes(entity))
-        if include_eid:
-            self._hiddens.append( ('eid', entity.eid, '') )
-        self._hiddens.append( (eid_param('__type', eid), entity.e_schema, '') )
-        w(u'<table id="%s" class="%s" style="width:100%%;">' %
-          (kwargs.get('tab_id', 'entityForm%s' % eid),
-           kwargs.get('tab_class', 'attributeForm')))
-        for widget in lines:
-            w(u'<tr>\n<th class="labelCol">%s</th>' % widget.render_label(entity))
-            error = widget.render_error(entity)
-            if error:
-                w(u'<td class="error" style="width:100%;">')
-            else:
-                w(u'<td style="width:100%;">')
-            if error:
-                w(error)
-            w(widget.edit_render(entity))
-            w(widget.render_help(entity))
-            w(u'</td>\n</tr>')
-        w(u'</table>')
-        return u'\n'.join(html)
-
-    def editable_attributes(self, entity):
-        # XXX both (add, delete)
-        return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add')
-                if rschema != 'eid']
-
-    def relations_form(self, entity, kwargs):
-        srels_by_cat = entity.srelations_by_category(('generic', 'metadata'), 'add')
-        if not srels_by_cat:
-            return u''
-        req = self.req
-        _ = self.req._
-        label = u'%s :' % _('This %s' % entity.e_schema).capitalize()
-        eid = entity.eid
-        html = []
-        w = html.append
-        w(u'<fieldset class="subentity">')
-        w(u'<legend class="iformTitle">%s</legend>' % label)
-        w(u'<table id="relatedEntities">')
-        for row in self.relations_table(entity):
-            # already linked entities
-            if row[2]:
-                w(u'<tr><th class="labelCol">%s</th>' % row[0].display_name(req, row[1]))
-                w(u'<td>')
-                w(u'<ul>')
-                for viewparams in row[2]:
-                    w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'
-                      % (viewparams[1], viewparams[0], viewparams[2], viewparams[3]))
-                if not self.force_display and self.maxrelitems < len(row[2]):
-                    w(u'<li class="invisible">%s</li>' % self.force_display_link())
-                w(u'</ul>')
-                w(u'</td>')
-                w(u'</tr>')
-        pendings = list(self.restore_pending_inserts(entity))
-        if not pendings:
-            w(u'<tr><th>&nbsp;</th><td>&nbsp;</td></tr>')
-        else:
-            for row in pendings:
-                # soon to be linked to entities
-                w(u'<tr id="tr%s">' % row[1])
-                w(u'<th>%s</th>' % row[3])
-                w(u'<td>')
-                w(u'<a class="handle" title="%s" href="%s">[x]</a>' %
-                  (_('cancel this insert'), row[2]))
-                w(u'<a id="a%s" class="editionPending" href="%s">%s</a>'
-                  % (row[1], row[4], xml_escape(row[5])))
-                w(u'</td>')
-                w(u'</tr>')
-        w(u'<tr id="relationSelectorRow_%s" class="separator">' % eid)
-        w(u'<th class="labelCol">')
-        w(u'<span>%s</span>' % _('add relation'))
-        w(u'<select id="relationSelector_%s" tabindex="%s" onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'
-          % (eid, req.next_tabindex(), xml_escape(dumps(eid))))
-        w(u'<option value="">%s</option>' % _('select a relation'))
-        for i18nrtype, rschema, target in srels_by_cat:
-            # more entities to link to
-            w(u'<option value="%s_%s">%s</option>' % (rschema, target, i18nrtype))
-        w(u'</select>')
-        w(u'</th>')
-        w(u'<td id="unrelatedDivs_%s"></td>' % eid)
-        w(u'</tr>')
-        w(u'</table>')
-        w(u'</fieldset>')
-        return '\n'.join(html)
-
-    def inline_entities_form(self, entity, kwargs):
-        """create a form to edit entity's inlined relations"""
-        result = []
-        _ = self.req._
-        for rschema, targettypes, x in entity.relations_by_category('inlineview', 'add'):
-            # show inline forms only if there's one possible target type
-            # for rschema
-            if len(targettypes) != 1:
-                self.warning('entity related by the %s relation should have '
-                             'inlined form but there is multiple target types, '
-                             'dunno what to do', rschema)
-                continue
-            targettype = targettypes[0].type
-            if self.should_inline_relation_form(entity, rschema, targettype, x):
-                result.append(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
-                    result.append(self.view('inline-edition', existant,
-                                            ptype=entity.e_schema, peid=entity.eid,
-                                            rtype=rschema, role=x, **kwargs))
-                if x == '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 self.should_display_inline_relation_form(rschema, existant, card):
-                    result.append(self.view('inline-creation', None, etype=targettype,
-                                            peid=entity.eid, ptype=entity.e_schema,
-                                            rtype=rschema, role=x, **kwargs))
-                # we can create more than one related entity, we thus display a link
-                # to add new related entities
-                if self.should_display_add_inline_relation_link(rschema, existant, card):
-                    divid = "addNew%s%s%s:%s" % (targettype, rschema, x, entity.eid)
-                    result.append(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
-                                  % divid)
-                    js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % (
-                        entity.eid, entity.e_schema, targettype, rschema, x)
-                    if card in '1?':
-                        js = "toggleVisibility('%s'); %s" % (divid, js)
-                    result.append(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
-                                  % (rschema, entity.eid, js,
-                                     self.req.__('add a %s' % targettype)))
-                    result.append(u'</div>')
-                    result.append(u'<div class="trame_grise">&nbsp;</div>')
-                result.append(u'</div>')
-        return '\n'.join(result)
-
-    # should_* method extracted to allow overriding
-
-    def should_inline_relation_form(self, entity, rschema, targettype, role):
-        return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role,
-                                                      targettype)
-
-    def should_display_inline_relation_form(self, rschema, existant, card):
-        return not existant and card in '1+'
-
-    def should_display_add_inline_relation_link(self, rschema, existant, card):
-        return not existant or card in '+*'
-
-    def reset_url(self, entity):
-        return entity.absolute_url()
-
-    def on_submit(self, entity):
-        return u'return freezeFormButtons(\'%s\')' % (self.domid)
-
-    def submited_message(self):
-        return self.req._('element edited')
-
-
-
-class CreationForm(EditionForm):
-    __select__ = specified_etype_implements('Any')
-    # XXX bw compat, use View.registered since we don't want accept_compat
-    #    wrapper set in EntityView
-    registered = accepts_etype_compat(View.registered)
-    id = 'creation'
-    title = _('creation')
-
-    def call(self, **kwargs):
-        """creation view for an entity"""
-        self.req.add_js( ('cubicweb.ajax.js',) )
-        self.initialize_varmaker()
-        etype = kwargs.pop('etype', self.req.form.get('etype'))
-        try:
-            entity = self.vreg.etype_class(etype)(self.req, None, None)
-        except:
-            self.w(self.req._('no such entity type %s') % etype)
-        else:
-            entity.eid = self.varmaker.next()
-            self.edit_form(entity, kwargs)
-
-    def action_title(self, entity):
-        """custom form title if creating a entity with __linkto"""
-        if '__linkto' in self.req.form:
-            if isinstance(self.req.form['__linkto'], list):
-                # XXX which one should be considered (case: add a ticket to a version in jpl)
-                rtype, linkto_eid, role = self.req.form['__linkto'][0].split(':')
-            else:
-                rtype, linkto_eid, role = self.req.form['__linkto'].split(':')
-            linkto_rset = self.req.eid_rset(linkto_eid)
-            linkto_type = linkto_rset.description[0][0]
-            if role == 'subject':
-                title = self.req.__('creating %s (%s %s %s %%(linkto)s)' % (
-                    entity.e_schema, entity.e_schema, rtype, linkto_type))
-            else:
-                title = self.req.__('creating %s (%s %%(linkto)s %s %s)' % (
-                    entity.e_schema, linkto_type, rtype, entity.e_schema))
-            msg = title % {'linkto' : self.view('incontext', linkto_rset)}
-            return u'<div class="formTitle notransform"><span>%s</span></div>' % msg
-        else:
-            return super(CreationForm, self).action_title(entity)
-
-    @property
-    def formid(self):
-        return 'edition'
-
-    def relations_form(self, entity, kwargs):
-        return u''
-
-    def reset_url(self, entity=None):
-        return self.build_url(self.req.form.get('etype', '').lower())
-
-    def submited_message(self):
-        return self.req._('element created')
-
-    def url(self):
-        """return the url associated with this view"""
-        return self.create_url(self.req.form.get('etype'))
-
-
-class InlineFormMixIn(object):
-
-    @cached
-    def card(self, etype):
-        return self.rschema.rproperty(self.parent_schema, etype, 'cardinality')[0]
-
-    def action_title(self, entity):
-        return self.rschema.display_name(self.req, self.role)
-
-    def add_hidden_web_behaviour_params(self, entity):
-        pass
-
-    def edit_form(self, entity, ptype, peid, rtype,
-                  role='subject', **kwargs):
-        self.rschema = self.schema.rschema(rtype)
-        self.role = role
-        self.parent_schema = self.schema.eschema(ptype)
-        self.parent_eid = peid
-        super(InlineFormMixIn, self).edit_form(entity, kwargs)
-
-    def should_inline_relation_form(self, entity, rschema, targettype, role):
-        if rschema == self.rschema:
-            return False
-        return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role,
-                                                      targettype)
-
-    @cached
-    def keep_entity(self, entity):
-        req = self.req
-        # are we regenerating form because of a validation error ?
-        erroneous_post = req.data.get('formvalues')
-        if erroneous_post:
-            cdvalues = req.list_form_param('%s:%s' % (self.rschema,
-                                                      self.parent_eid),
-                                           erroneous_post)
-            if unicode(entity.eid) not in cdvalues:
-                return False
-        return True
-
-    def form_context(self, entity, kwargs):
-        ctx = super(InlineFormMixIn, self).form_context(entity, kwargs)
-        _ = self.req._
-        local_ctx = {'createmsg' : self.req.__('add a %s' % entity.e_schema),
-                     'so': self.role[0], # 's' for subject, 'o' for object
-                     'eid' : entity.eid,
-                     'rtype' : self.rschema,
-                     'parenteid' : self.parent_eid,
-                     'parenttype' : self.parent_schema,
-                     'etype' : entity.e_schema,
-                     'novalue' : INTERNAL_FIELD_VALUE,
-                     'removemsg' : self.req.__('remove this %s' % entity.e_schema),
-                     'notice' : self.req._('click on the box to cancel the deletion'),
-                     }
-        ctx.update(local_ctx)
-        return ctx
-
-
-class CopyEditionForm(EditionForm):
-    id = 'copy'
-    title = _('copy edition')
-
-    def cell_call(self, row, col, **kwargs):
-        self.req.add_js(('cubicweb.ajax.js',))
-        entity = self.complete_entity(row, col, skip_bytes=True)
-        # make a copy of entity to avoid altering the entity in the
-        # request's cache.
-        self.newentity = copy(entity)
-        self.copying = self.newentity.eid
-        self.newentity.eid = None
-        self.edit_form(self.newentity, kwargs)
-        del self.newentity
-
-    def action_title(self, entity):
-        """form's title"""
-        msg = super(CopyEditionForm, self).action_title(entity)
-        return msg + (u'<script type="text/javascript">updateMessage("%s");</script>\n'
-                      % self.req._('Please note that this is only a shallow copy'))
-        # XXX above message should have style of a warning
-
-    @property
-    def formid(self):
-        return 'edition'
-
-    def relations_form(self, entity, kwargs):
-        return u''
-
-    def reset_url(self, entity):
-        return self.build_url('view', rql='Any X WHERE X eid %s' % self.copying)
-
-    def attributes_form(self, entity, kwargs, include_eid=True):
-        # we don't want __clone_eid on inlined edited entities
-        if entity.eid == self.newentity.eid:
-            self._hiddens.append((eid_param('__cloned_eid', entity.eid), self.copying, ''))
-        return EditionForm.attributes_form(self, entity, kwargs, include_eid)
-
-    def submited_message(self):
-        return self.req._('element copied')
-
-
-class TableEditForm(FormMixIn, EntityView):
-    id = 'muledit'
-    title = _('multiple edit')
-
-    EDITION_BODY = u'''<form method="post" id="entityForm" onsubmit="return validateForm('entityForm', null);" action="%(action)s">
-  %(error)s
-  <div id="progress">%(progress)s</div>
-  <fieldset>
-  <input type="hidden" name="__errorurl" value="%(url)s" />
-  <input type="hidden" name="__form_id" value="%(formid)s" />
-  <input type="hidden" name="__redirectvid" value="%(redirectvid)s" />
-  <input type="hidden" name="__redirectrql" value="%(redirectrql)s" />
-  <table class="listing">
-    <tr class="header">
-      <th align="left"><input type="checkbox" onclick="setCheckboxesState('eid', this.checked)" value="" title="toggle check boxes" /></th>
-      %(attrheaders)s
-    </tr>
-    %(lines)s
-  </table>
-  <table width="100%%">
-    <tr>
-      <td align="left">
-        <input class="validateButton" type="submit"  value="%(okvalue)s" title="%(oktitle)s" />
-        <input class="validateButton" type="reset" name="__action_cancel" value="%(cancelvalue)s" title="%(canceltitle)s" />
-      </td>
-    </tr>
-  </table>
-  </fieldset>
-</form>
-'''
-
-    WIDGET_CELL = u'''\
-<td%(csscls)s>
-  %(error)s
-  <div>%(widget)s</div>
-</td>'''
-
-    def call(self, **kwargs):
-        """a view to edit multiple entities of the same type
-        the first column should be the eid
-        """
-        req = self.req
-        form = req.form
-        _ = req._
-        sampleentity = self.complete_entity(0)
-        attrheaders = [u'<th>%s</th>' % rdef[0].display_name(req, rdef[-1])
-                       for rdef in sampleentity.relations_by_category('primary', 'add')
-                       if rdef[0].type != 'eid']
-        ctx = {'action' : self.build_url('edit'),
-               'error': self.error_message(),
-               'progress': _('validating...'),
-               'url': xml_escape(req.url()),
-               'formid': self.id,
-               'redirectvid': xml_escape(form.get('__redirectvid', 'list')),
-               'redirectrql': xml_escape(form.get('__redirectrql', self.rset.printable_rql())),
-               'attrheaders': u'\n'.join(attrheaders),
-               'lines': u'\n'.join(self.edit_form(ent) for ent in self.rset.entities()),
-               'okvalue': _('button_ok').capitalize(),
-               'oktitle': _('validate modifications on selected items').capitalize(),
-               'cancelvalue': _('button_reset').capitalize(),
-               'canceltitle': _('revert changes').capitalize(),
-               }
-        self.w(self.EDITION_BODY % ctx)
-
-
-    def reset_url(self, entity=None):
-        self.build_url('view', rql=self.rset.printable_rql())
-
-    def edit_form(self, entity):
-        html = []
-        w = html.append
-        entity.complete()
-        eid = entity.eid
-        values = self.req.data.get('formvalues', ())
-        qeid = eid_param('eid', eid)
-        checked = qeid in values
-        w(u'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
-        w(u'<td>%s<input type="hidden" name="__type:%s" value="%s" /></td>'
-          % (checkbox('eid', eid, checked=checked), eid, entity.e_schema))
-        # attribute relations (skip eid which is handled by the checkbox
-        wdg = entity.get_widget
-        wdgfactories = [wdg(rschema, x) for rschema, _, x in entity.relations_by_category('primary', 'add')
-                        if rschema.type != 'eid'] # XXX both (add, delete)
-        seid = xml_escape(dumps(eid))
-        for wobj in wdgfactories:
-            if isinstance(wobj, ComboBoxWidget):
-                wobj.attrs['onchange'] = "setCheckboxesState2('eid', %s, 'checked')" % seid
-            elif isinstance(wobj, InputWidget):
-                wobj.attrs['onkeypress'] = "setCheckboxesState2('eid', %s, 'checked')" % seid
-            error = wobj.render_error(entity)
-            if error:
-                csscls = u' class="error"'
-            else:
-                csscls = u''
-            w(self.WIDGET_CELL % {'csscls': csscls, 'error': error,
-                                  'widget': wobj.edit_render(entity)})
-        w(u'</tr>')
-        return '\n'.join(html)
-
-
-# XXX bw compat
-
-from logilab.common.deprecation import class_moved
-from cubicweb.web.views import editviews
-ComboboxView = class_moved(editviews.ComboboxView)
--- a/web/views/basetemplates.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/basetemplates.py	Fri Aug 14 00:02:08 2009 +0200
@@ -113,9 +113,9 @@
         if vtitle:
             w(u'<h1 class="vtitle">%s</h1>\n' % xml_escape(vtitle))
         # display entity type restriction component
-        etypefilter = self.vreg['components'].select_vobject(
+        etypefilter = self.vreg['components'].select_or_none(
             'etypenavigation', self.req, rset=self.rset)
-        if etypefilter:
+        if etypefilter and etypefilter.cw_propval('visible'):
             etypefilter.render(w=w)
         self.nav_html = UStringIO()
         if view and view.need_navigation:
@@ -154,12 +154,11 @@
         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['components'].select_object('rqlinput', self.req,
-                                                        rset=self.rset)
+        components = self.vreg['components']
+        rqlcomp = components.select_or_none('rqlinput', self.req, rset=self.rset)
         if rqlcomp:
             rqlcomp.render(w=self.w, view=view)
-        msgcomp = self.vreg['components'].select_object('applmessages',
-                                                        self.req, rset=self.rset)
+        msgcomp = components.select_or_none('applmessages', self.req, rset=self.rset)
         if msgcomp:
             msgcomp.render(w=self.w)
         self.content_header(view)
@@ -173,7 +172,7 @@
         self.w(u'</body>')
 
     def nav_column(self, view, context):
-        boxes = list(self.vreg['boxes'].possible_vobjects(
+        boxes = list(self.vreg['boxes'].poss_visible_objects(
             self.req, rset=self.rset, view=view, context=context))
         if boxes:
             self.w(u'<td class="navcol"><div class="navboxes">\n')
@@ -242,7 +241,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['boxes'].possible_vobjects(
+        boxes = list(self.vreg['boxes'].poss_visible_objects(
             self.req, rset=self.rset, view=view, context='left'))
         if boxes:
             w(u'<div class="navboxes">\n')
@@ -257,9 +256,9 @@
             w(u'<h1 class="vtitle">%s</h1>' % xml_escape(vtitle))
 
     def topleft_header(self):
-        logo = self.vreg['components'].select_vobject('logo', self.req,
+        logo = self.vreg['components'].select_or_none('logo', self.req,
                                                       rset=self.rset)
-        if logo:
+        if logo and logo.cw_propval('visible'):
             self.w(u'<table id="header"><tr>\n')
             self.w(u'<td>')
             logo.render(w=self.w)
@@ -299,8 +298,8 @@
             self.req.add_js(jscript, localfile=False)
 
     def alternates(self):
-        urlgetter = self.vreg['components'].select_object('rss_feed_url',
-                                            self.req, rset=self.rset)
+        urlgetter = self.vreg['components'].select_or_none('rss_feed_url',
+                                                           self.req, rset=self.rset)
         if urlgetter is not None:
             self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n'
                        %  xml_escape(urlgetter.feed_url()))
@@ -329,29 +328,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">')
-        logo = self.vreg['components'].select_vobject(
+        logo = self.vreg['components'].select_or_none(
             'logo', self.req, rset=self.rset)
-        if logo:
+        if logo and logo.cw_propval('visible'):
             logo.render(w=self.w)
         self.w(u'</td>\n')
         # appliname and breadcrumbs
         self.w(u'<td id="headtext">')
         for cid in ('appliname', 'breadcrumbs'):
-            comp = self.vreg['components'].select_vobject(
+            comp = self.vreg['components'].select_or_none(
                 cid, self.req, rset=self.rset)
-            if comp:
+            if comp and comp.cw_propval('visible'):
                 comp.render(w=self.w)
         self.w(u'</td>')
         # logged user and help
         self.w(u'<td>\n')
-        comp = self.vreg['components'].select_vobject(
+        comp = self.vreg['components'].select_or_none(
             'loggeduserlink', self.req, rset=self.rset)
-        if comp:
+        if comp and comp.cw_propval('visible'):
             comp.render(w=self.w)
         self.w(u'</td><td>')
-        helpcomp = self.vreg['components'].select_vobject(
+        helpcomp = self.vreg['components'].select_or_none(
             'help', self.req, rset=self.rset)
-        if helpcomp:
+        if helpcomp and helpcomp.cw_propval('visible'):
             helpcomp.render(w=self.w)
         self.w(u'</td>')
         # lastcolumn
@@ -405,7 +404,7 @@
 
     def call(self, view, **kwargs):
         """by default, display informal messages in content header"""
-        components = self.vreg['contentnavigation'].possible_vobjects(
+        components = self.vreg['contentnavigation'].poss_visible_objects(
             self.req, rset=self.rset, view=view, context='navtop')
         if components:
             self.w(u'<div id="contentheader">')
@@ -421,7 +420,7 @@
     id = 'contentfooter'
 
     def call(self, view, **kwargs):
-        components = self.vreg['contentnavigation'].possible_vobjects(
+        components = self.vreg['contentnavigation'].poss_visible_objects(
             self.req, rset=self.rset, view=view, context='navbottom')
         if components:
             self.w(u'<div id="contentfooter">')
--- a/web/views/baseviews.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/baseviews.py	Fri Aug 14 00:02:08 2009 +0200
@@ -110,7 +110,7 @@
         """the secondary view for an entity
         secondary = icon + view(oneline)
         """
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'&nbsp;')
         self.wview('oneline', self.rset, row=row, col=col)
 
@@ -122,7 +122,7 @@
     def cell_call(self, row, col):
         """the one line view for an entity: linked text view
         """
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<a href="%s">' % xml_escape(entity.absolute_url()))
         self.w(xml_escape(self.view('text', self.rset, row=row, col=col)))
         self.w(u'</a>')
@@ -150,7 +150,7 @@
                 self.w(u"\n")
 
     def cell_call(self, row, col=0, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(cut(entity.dc_title(),
                    self.req.property_value('navigation.short-line-size')))
 
@@ -162,7 +162,7 @@
 
     def cell_call(self, row, col):
         _ = self.req._
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<div class="metadata">')
         if self.show_eid:
             self.w(u'#%s - ' % entity.eid)
@@ -185,7 +185,7 @@
     id = 'textincontext'
     title = None # not listed as a possible view
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(entity.dc_title())
 
 
@@ -193,7 +193,7 @@
     id = 'textoutofcontext'
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(entity.dc_long_title())
 
 
@@ -201,7 +201,7 @@
     id = 'incontext'
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         desc = cut(entity.dc_description(), 50)
         self.w(u'<a href="%s" title="%s">' % (
             xml_escape(entity.absolute_url()), xml_escape(desc)))
@@ -214,7 +214,7 @@
     id = 'outofcontext'
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         desc = cut(entity.dc_description(), 50)
         self.w(u'<a href="%s" title="%s">' % (
             xml_escape(entity.absolute_url()), xml_escape(desc)))
--- a/web/views/editcontroller.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/editcontroller.py	Fri Aug 14 00:02:08 2009 +0200
@@ -198,7 +198,7 @@
             # if it is a file, transport it using a Binary (StringIO)
             # XXX later __detach is for the new widget system, the former is to
             # be removed once web/widgets.py has been dropped
-            if formparams.has_key('__%s_detach' % attr) or formparams.has_key('%s__detach' % attr):
+            if formparams.has_key('%s__detach' % attr):
                 # drop current file value
                 value = None
             # no need to check value when nor explicit detach nor new file
--- a/web/views/editforms.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/editforms.py	Fri Aug 14 00:02:08 2009 +0200
@@ -162,7 +162,7 @@
         assert role in ('subject', 'object')
         if default is None:
             default = xml_escape(self.req._('<no value>'))
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         rschema = entity.schema.rschema(rtype)
         lzone = self._build_landing_zone(landing_zone)
         # compute value, checking perms, build form
@@ -457,7 +457,7 @@
         :param rtype: the relation bridging `etype` and `peid`
         :param role: the role played by the `peid` in the relation
         """
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (peid, rtype,
                                                                  entity.eid)
         self.render_form(entity, peid, rtype, role, divonclick=divonclick)
--- a/web/views/editviews.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/editviews.py	Fri Aug 14 00:02:08 2009 +0200
@@ -43,7 +43,7 @@
 
     @cached
     def filter_box_context_info(self):
-        entity = self.entity(0, 0)
+        entity = self.rset.get_entity(0, 0)
         role, eid, rtype, etype = self.req.search_state[1]
         assert entity.eid == typed_eid(eid)
         # the default behaviour is to fetch all unrelated entities and display
@@ -60,7 +60,7 @@
 class OutOfContextSearch(EntityView):
     id = 'outofcontext-search'
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         erset = entity.as_rset()
         if self.req.match_search_state(erset):
             self.w(u'<a href="%s" title="%s">%s</a>&nbsp;<a href="%s" title="%s">[...]</a>' % (
@@ -78,7 +78,7 @@
     __select__ = match_form_params('relation')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         relname, target = self.req.form.get('relation').rsplit('_', 1)
         rschema = self.schema.rschema(relname)
         hidden = 'hidden' in self.req.form
--- a/web/views/emailaddress.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/emailaddress.py	Fri Aug 14 00:02:08 2009 +0200
@@ -76,7 +76,7 @@
     __select__ = implements('EmailAddress')
 
     def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         if entity.reverse_primary_email:
             self.w(u'<b>')
         if entity.alias:
@@ -96,7 +96,7 @@
     __select__ = implements('EmailAddress')
 
     def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         if entity.reverse_primary_email:
             self.w(u'<b>')
         if entity.alias:
@@ -119,4 +119,4 @@
     __select__ = implements('EmailAddress')
 
     def cell_call(self, row, col, **kwargs):
-        self.w(self.entity(row, col).display_address())
+        self.w(self.rset.get_entity(row, col).display_address())
--- a/web/views/embedding.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/embedding.py	Fri Aug 14 00:02:08 2009 +0200
@@ -12,10 +12,10 @@
 import re
 from urlparse import urljoin
 from urllib2 import urlopen, Request, HTTPError
+from urllib import quote as urlquote # XXX should use view.url_quote method
 
 from logilab.mtconverter import guess_encoding
 
-from cubicweb import urlquote # XXX should use view.url_quote method
 from cubicweb.selectors import (one_line_rset, score_entity,
                                 match_search_state, implements)
 from cubicweb.interfaces import IEmbedable
@@ -98,14 +98,13 @@
                   & score_entity(entity_has_embedable_url))
 
     title = _('embed')
-    controller = 'embed'
 
     def url(self, row=0):
         entity = self.rset.get_entity(row, 0)
         url = urljoin(self.req.base_url(), entity.embeded_url())
         if self.req.form.has_key('rql'):
-            return self.build_url(url=url, rql=self.req.form['rql'])
-        return self.build_url(url=url)
+            return self.build_url('embed', url=url, rql=self.req.form['rql'])
+        return self.build_url('embed', url=url)
 
 
 
--- a/web/views/facets.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/facets.py	Fri Aug 14 00:02:08 2009 +0200
@@ -77,7 +77,7 @@
             mainvar, baserql = prepare_facets_rqlst(rqlst, rset.args)
             widgets = []
             for facet in self.get_facets(rset, mainvar):
-                if facet.propval('visible'):
+                if facet.cw_propval('visible'):
                     wdg = facet.get_widget()
                     if wdg is not None:
                         widgets.append(wdg)
@@ -118,9 +118,9 @@
             self.w(self.bk_linkbox_template % bk_link)
 
     def get_facets(self, rset, mainvar):
-        return self.vreg['facets'].possible_vobjects(self.req, rset=rset,
-                                                     context='facetbox',
-                                                     filtered_variable=mainvar)
+        return self.vreg['facets'].poss_visible_objects(self.req, rset=rset,
+                                                        context='facetbox',
+                                                        filtered_variable=mainvar)
 
 # facets ######################################################################
 
--- a/web/views/formrenderers.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/formrenderers.py	Fri Aug 14 00:02:08 2009 +0200
@@ -15,11 +15,16 @@
 from cubicweb.common import tags
 from cubicweb.appobject import AppObject
 from cubicweb.selectors import entity_implements, yes
-from cubicweb.web import eid_param
-from cubicweb.web import formwidgets as fwdgs
-from cubicweb.web.widgets import checkbox
+from cubicweb.web import eid_param, formwidgets as fwdgs
 from cubicweb.web.formfields import HiddenInitialValueField
 
+def checkbox(name, value, attrs='', checked=None):
+    if checked is None:
+        checked = value
+    checked = checked and 'checked="checked"' or ''
+    return u'<input type="checkbox" name="%s" value="%s" %s %s />' % (
+        name, value, checked, attrs)
+
 
 class FormRenderer(AppObject):
     """basic renderer displaying fields in a two columns table label | value
--- a/web/views/ibreadcrumbs.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/ibreadcrumbs.py	Fri Aug 14 00:02:08 2009 +0200
@@ -36,7 +36,7 @@
     separator = u'&nbsp;&gt;&nbsp;'
 
     def call(self, view=None, first_separator=True):
-        entity = self.entity(0)
+        entity = self.rset.get_entity(0,0)
         path = entity.breadcrumbs(view)
         if path:
             self.w(u'<span class="pathbar">')
@@ -80,7 +80,7 @@
     id = 'breadcrumbs'
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         desc = xml_escape(cut(entity.dc_description(), 50))
         self.w(u'<a href="%s" title="%s">%s</a>' % (
             xml_escape(entity.absolute_url()), desc, bc_title(entity)))
--- a/web/views/idownloadable.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/idownloadable.py	Fri Aug 14 00:02:08 2009 +0200
@@ -50,7 +50,7 @@
     order = 10
 
     def cell_call(self, row, col, title=None, label=None, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         download_box(self.w, entity, title, label)
 
 
@@ -91,7 +91,7 @@
 
 
     def cell_call(self, row, col, title=None, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         url = xml_escape(entity.download_url())
         self.w(u'<a href="%s">%s</a>' % (url, xml_escape(title or entity.dc_title())))
 
@@ -123,7 +123,7 @@
 
     def cell_call(self, row, col, title=None, **kwargs):
         """the oneline view is a link to download the file"""
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         url = xml_escape(entity.absolute_url())
         name = xml_escape(title or entity.download_file_name())
         durl = xml_escape(entity.download_url())
@@ -145,7 +145,7 @@
             self.w(u'</div>')
 
     def cell_call(self, row, col, width=None, height=None, link=False):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         #if entity.data_format.startswith('image/'):
         imgtag = u'<img src="%s" alt="%s" ' % (xml_escape(entity.download_url()),
                                                xml_escape(entity.download_file_name()))
--- a/web/views/igeocodable.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/igeocodable.py	Fri Aug 14 00:02:08 2009 +0200
@@ -40,7 +40,7 @@
         self.w(simplejson.dumps(geodata))
 
     def build_marker_data(self, row, extraparams):
-        entity = self.entity(row, 0)
+        entity = self.rset.get_entity(row, 0)
         return {'latitude': entity.latitude, 'longitude': entity.longitude,
                 'title': entity.dc_long_title(),
                 #icon defines : (icon._url, icon.size,  icon.iconAncho', icon.shadow)
@@ -55,7 +55,7 @@
     __select__ = implements(IGeocodable)
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<div>%s</div>' % entity.view('oneline'))
         # FIXME: we should call something like address-view if available
 
--- a/web/views/iprogress.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/iprogress.py	Fri Aug 14 00:02:08 2009 +0200
@@ -59,7 +59,7 @@
 
     def cell_call(self, row, col):
         _ = self.req._
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         infos = {}
         for col in self.columns:
             meth = getattr(self, 'build_%s_cell' % col, None)
@@ -186,7 +186,7 @@
 
     def cell_call(self, row, col):
         self.req.add_css('cubicweb.iprogress.css')
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         widget = ProgressBarWidget(entity.done, entity.todo,
                                    entity.revised_cost)
         self.w(widget.render())
--- a/web/views/management.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/management.py	Fri Aug 14 00:02:08 2009 +0200
@@ -79,7 +79,7 @@
     def cell_call(self, row, col):
         self.req.add_js('cubicweb.edition.js')
         self.req.add_css('cubicweb.acl.css')
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         w = self.w
         _ = self.req._
         w(u'<h1><span class="etype">%s</span> <a href="%s">%s</a></h1>'
--- a/web/views/navigation.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/navigation.py	Fri Aug 14 00:02:08 2009 +0200
@@ -149,8 +149,8 @@
 def limit_rset_using_paged_nav(self, req, rset, w, forcedisplay=False,
                                show_all_option=True, page_size=None):
     if not (forcedisplay or req.form.get('__force_display') is not None):
-        nav = self.vreg['components'].select_object('navigation', req,
-                                      rset=rset, page_size=page_size)
+        nav = self.vreg['components'].select_or_none('navigation', req,
+                                                     rset=rset, page_size=page_size)
         if nav:
             # get boundaries before component rendering
             start, stop = nav.page_boundaries()
@@ -187,7 +187,7 @@
     context = 'navbottom'
     order = 10
     def call(self, view=None):
-        entity = self.entity(0)
+        entity = self.rset.get_entity(0,0)
         previous = entity.previous_entity()
         next = entity.next_entity()
         if previous or next:
--- a/web/views/old_calendar.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/old_calendar.py	Fri Aug 14 00:02:08 2009 +0200
@@ -109,7 +109,7 @@
         self.req.add_css('cubicweb.calendar.css')
         schedule = {}
         for row in xrange(len(self.rset.rows)):
-            entity = self.entity(row)
+            entity = self.rset.get_entity(row,0)
             infos = u'<div class="event">'
             infos += self.view(itemvid, self.rset, row=row)
             infos += u'</div>'
--- a/web/views/primary.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/primary.py	Fri Aug 14 00:02:08 2009 +0200
@@ -71,7 +71,7 @@
 
     def content_navigation_components(self, context):
         self.w(u'<div class="%s">' % context)
-        for comp in self.vreg['contentnavigation'].possible_vobjects(
+        for comp in self.vreg['contentnavigation'].poss_visible_objects(
             self.req, rset=self.rset, row=self.row, view=self, context=context):
             try:
                 comp.render(w=self.w, row=self.row, view=self)
@@ -147,7 +147,7 @@
             label = display_name(self.req, rschema.type, role)
             vid = dispctrl.get('vid', 'sidebox')
             sideboxes.append( (label, rset, vid) )
-        sideboxes += self.vreg['boxes'].possible_vobjects(
+        sideboxes += self.vreg['boxes'].poss_visible_objects(
             self.req, rset=self.rset, row=self.row, view=self,
             context='incontext')
         return sideboxes
--- a/web/views/schema.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/schema.py	Fri Aug 14 00:02:08 2009 +0200
@@ -199,7 +199,7 @@
     __select__ = implements('CWEType')
 
     def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         final = entity.final
         if final:
             self.w(u'<em class="finalentity">')
@@ -227,7 +227,7 @@
     __select__ = EntityView.__select__ & implements('CWEType')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<h2>%s</h2>' % _('Attributes'))
         rset = self.req.execute('Any N,F,D,I,J,DE,A '
                                 'ORDERBY AA WHERE A is CWAttribute, '
@@ -263,7 +263,7 @@
     __select__ = EntityView.__select__ & implements('CWEType')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         url = entity.absolute_url(vid='schemagraph')
         self.w(u'<img src="%s" alt="%s"/>' % (
             xml_escape(url),
@@ -274,7 +274,7 @@
     __select__ = EntityView.__select__ & implements('CWEType')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<h2>%s</h2>' % _('Add permissions'))
         rset = self.req.execute('Any P WHERE X add_permission P, '
                                 'X eid %(x)s',
@@ -301,7 +301,7 @@
     __select__ = EntityView.__select__ & implements('CWEType')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         if entity.reverse_state_of:
             self.w(u'<img src="%s" alt="%s"/>' % (
                     xml_escape(entity.absolute_url(vid='ewfgraph')),
@@ -377,7 +377,7 @@
 
     def _generate(self, tmpfile):
         """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
+        entity = self.rset.get_entity(self.row, self.col)
         eschema = self.vreg.schema.eschema(entity.name)
         visitor = OneHopESchemaVisitor(self.req, eschema,
                                        skiptypes=skip_types(self.req))
@@ -389,7 +389,7 @@
 
     def _generate(self, tmpfile):
         """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
+        entity = self.rset.get_entity(self.row, self.col)
         rschema = self.vreg.schema.rschema(entity.name)
         visitor = OneHopRSchemaVisitor(self.req, rschema)
         s2d.schema2dot(outputfile=tmpfile, visitor=visitor)
--- a/web/views/sparql.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/sparql.py	Fri Aug 14 00:02:08 2009 +0200
@@ -108,7 +108,7 @@
                                        datatype=xmlschema(celltype)),
                              name=varname)
         else:
-            entity = self.entity(row, col)
+            entity = self.rset.get_entity(row, col)
             return E.binding(E.uri(entity.absolute_url()), name=varname)
 
     def set_request_content_type(self):
--- a/web/views/startup.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/startup.py	Fri Aug 14 00:02:08 2009 +0200
@@ -22,16 +22,6 @@
     title = _('manage')
     http_cache_manager = httpcache.EtagHTTPCacheManager
 
-    @classmethod
-    def vreg_initialization_completed(cls):
-        for eschema in cls.schema.entities():
-            if eschema.schema_entity():
-                uicfg.indexview_etype_section.setdefault(eschema, 'schema')
-            elif eschema.is_subobject(strict=True):
-                uicfg.indexview_etype_section.setdefault(eschema, 'subobject')
-            else:
-                uicfg.indexview_etype_section.setdefault(eschema, 'application')
-
     def display_folders(self):
         return False
 
--- a/web/views/tableview.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/tableview.py	Fri Aug 14 00:02:08 2009 +0200
@@ -34,7 +34,7 @@
             return ()
         rqlst.save_state()
         mainvar, baserql = prepare_facets_rqlst(rqlst, self.rset.args)
-        wdgs = [facet.get_widget() for facet in self.vreg['facets'].possible_vobjects(
+        wdgs = [facet.get_widget() for facet in self.vreg['facets'].poss_visible_objects(
             self.req, rset=self.rset, context='tablefilter',
             filtered_variable=mainvar)]
         wdgs = [wdg for wdg in wdgs if wdg is not None]
@@ -293,7 +293,7 @@
              displaycols=None, displayactions=None, mainindex=None):
         """Dumps a table displaying a composite query"""
         actrql = self.req.form['actualrql']
-        self.ensure_ro_rql(actrql)
+        self.req.ensure_ro_rql(actrql)
         displaycols = self.displaycols(displaycols)
         if displayactions is None and 'displayactions' in self.req.form:
             displayactions = True
--- a/web/views/tabs.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/tabs.py	Fri Aug 14 00:02:08 2009 +0200
@@ -160,7 +160,7 @@
     vid = 'list'
 
     def cell_call(self, row, col):
-        rset = self.entity(row, col).related(self.rtype, role(self))
+        rset = self.rset.get_entity(row, col).related(self.rtype, role(self))
         self.w(u'<div class="mainInfo">')
         if self.title:
             self.w(tags.h1(self.req._(self.title)))
--- a/web/views/treeview.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/treeview.py	Fri Aug 14 00:02:08 2009 +0200
@@ -63,7 +63,7 @@
     id = 'filetree-oneline'
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf():
             self.w(u'<div class="folder">%s</div>\n' % entity.view('oneline'))
         else:
@@ -77,7 +77,7 @@
 
     def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None):
         assert treeid is not None
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         itemview = self.view(vid, self.rset, row=row, col=col)
         if row == len(self.rset) - 1:
             self.w(u'<li class="last">%s</li>' % itemview)
@@ -102,7 +102,7 @@
 
     def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'):
         w = self.w
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         liclasses = []
         is_last = row == len(self.rset) - 1
         is_open = self.open_state(entity.eid, treeid)
--- a/web/views/urlpublishing.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/urlpublishing.py	Fri Aug 14 00:02:08 2009 +0200
@@ -217,6 +217,7 @@
             raise PathDontMatch()
         # remove last part and see if this is something like an actions
         # if so, call
+        # XXX bad smell: refactor to simpler code
         try:
             actionsreg = self.vreg['actions']
             requested = parts.pop(-1)
@@ -232,7 +233,7 @@
                 continue
             else:
                 try:
-                    action = actionsreg.select_best(actions, req, rset=rset)
+                    action = actionsreg._select_best(actions, req, rset=rset)
                 except RegistryException:
                     continue
                 else:
--- a/web/views/workflow.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/workflow.py	Fri Aug 14 00:02:08 2009 +0200
@@ -47,7 +47,7 @@
     __select__ = implements(IWorkflowable) & match_form_params('treid')
 
     def cell_call(self, row, col):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         state = entity.in_state[0]
         transition = self.req.entity_from_eid(self.req.form['treid'])
         dest = transition.destination()
@@ -118,7 +118,7 @@
     __select__ = implements('TrInfo')
 
     def cell_call(self, row, col, cellvid=None):
-        self.w(self.entity(row, col).view('reledit', rtype='comment'))
+        self.w(self.rset.get_entity(row, col).view('reledit', rtype='comment'))
 
 
 class StateInContextView(view.EntityView):
@@ -150,7 +150,7 @@
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
 
     def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
+        entity = self.rset.get_entity(row, col)
         self.w(u'<h1>%s</h1>' % (self.req._('workflow for %s')
                                  % display_name(self.req, entity.name)))
         self.w(u'<img src="%s" alt="%s"/>' % (
@@ -217,7 +217,7 @@
 
     def _generate(self, tmpfile):
         """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
+        entity = self.rset.get_entity(self.row, self.col)
         visitor = WorkflowVisitor(entity)
         prophdlr = WorkflowDotPropsHandler(self.req)
         generator = GraphGenerator(DotBackend('workflow', 'LR',
--- a/web/views/xmlrss.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/views/xmlrss.py	Fri Aug 14 00:02:08 2009 +0200
@@ -117,7 +117,7 @@
     __select__ = non_final_entity() & one_line_rset()
 
     def feed_url(self):
-        return self.entity(0, 0).rss_feed_url()
+        return self.rset.get_entity(0, 0).rss_feed_url()
 
 
 class RSSIconBox(box.BoxTemplate):
--- a/web/webctl.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/web/webctl.py	Fri Aug 14 00:02:08 2009 +0200
@@ -8,8 +8,7 @@
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb import underline_title
-from cubicweb.toolsutils import CommandHandler
+from cubicweb.toolsutils import CommandHandler, underline_title
 from logilab.common.shellutils import ASK
 
 class WebCreateHandler(CommandHandler):
--- a/web/widgets.py	Fri Aug 14 00:01:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,930 +0,0 @@
-"""widgets for entity edition
-
-those are in cubicweb.common since we need to know available widgets at schema
-serialization time
-
-: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"
-
-from datetime import datetime
-
-from logilab.mtconverter import xml_escape
-
-from yams.constraints import SizeConstraint, StaticVocabularyConstraint
-
-from cubicweb.common.uilib import toggle_action
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
-
-def _format_attrs(kwattrs):
-    """kwattrs is the dictionary of the html attributes available for
-    the edited element
-    """
-    # sort for predictability (required for tests)
-    return u' '.join(sorted(u'%s="%s"' % item for item in kwattrs.iteritems()))
-
-def _value_from_values(values):
-    # take care, value may be 0, 0.0...
-    if values:
-        value = values[0]
-        if value is None:
-            value = u''
-    else:
-        value = u''
-    return value
-
-def _eclass_eschema(eschema_or_eclass):
-    try:
-        return eschema_or_eclass, eschema_or_eclass.e_schema
-    except AttributeError:
-        return None, eschema_or_eclass
-
-def checkbox(name, value, attrs='', checked=None):
-    if checked is None:
-        checked = value
-    checked = checked and 'checked="checked"' or ''
-    return u'<input type="checkbox" name="%s" value="%s" %s %s />' % (
-        name, value, checked, attrs)
-
-def widget(vreg, subjschema, rschema, objschema, role='object'):
-    """get a widget to edit the given relation"""
-    if rschema == 'eid':
-        # return HiddenWidget(vreg, subjschema, rschema, objschema)
-        return EidWidget(vreg, _eclass_eschema(subjschema)[1], rschema, objschema)
-    return widget_factory(vreg, subjschema, rschema, objschema, role=role)
-
-
-class Widget(object):
-    """abstract widget class"""
-    need_multipart = False
-    # generate the "id" attribute with the same value as the "name" (html) attribute
-    autoid = True
-    html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress'))
-    cubicwebns_attributes = set()
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 role='subject', description=None,
-                 **kwattrs):
-        self.vreg = vreg
-        self.rschema = rschema
-        self.subjtype = subjschema
-        self.objtype = objschema
-        self.role = role
-        self.name = rschema.type
-        self.description = description
-        self.attrs = kwattrs
-        # XXX accesskey may not be unique
-        kwattrs['accesskey'] = self.name[0]
-
-    def copy(self):
-        """shallow copy (useful when you need to modify self.attrs
-        because widget instances are cached)
-        """
-        # brute force copy (subclasses don't have the
-        # same __init__ prototype)
-        widget = self.__new__(self.__class__)
-        widget.__dict__ = dict(self.__dict__)
-        widget.attrs = dict(widget.attrs)
-        return widget
-
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        pass
-
-    def format_attrs(self):
-        """return a string with html attributes available for the edit input"""
-        # sort for predictability (required for tests)
-        attrs = []
-        for name, value in self.attrs.iteritems():
-            # namespace attributes have priority over standard xhtml ones
-            if name in self.cubicwebns_attributes:
-                attrs.append(u'cubicweb:%s="%s"' % (name, value))
-            elif name in self.html_attributes:
-                attrs.append(u'%s="%s"' % (name, value))
-        return u' '.join(sorted(attrs))
-
-    def required(self, entity):
-        """indicates if the widget needs a value to be filled in"""
-        card = self.rschema.cardinality(self.subjtype, self.objtype, self.role)
-        return card in '1+'
-
-    def input_id(self, entity):
-        try:
-            return self.rname
-        except AttributeError:
-            return eid_param(self.name, entity.eid)
-
-    def render_label(self, entity, label=None):
-        """render widget's label"""
-        label = label or self.rschema.display_name(entity.req, self.role)
-        forid = self.input_id(entity)
-        if forid:
-            forattr =  ' for="%s"' % forid
-        else:
-            forattr = ''
-        if self.required(entity):
-            label = u'<label class="required"%s>%s</label>' % (forattr, label)
-        else:
-            label = u'<label%s>%s</label>' % (forattr, label)
-        return label
-
-    def render_error(self, entity):
-        """return validation error for widget's field of the given entity, if
-        any
-        """
-        errex = entity.req.data.get('formerrors')
-        if errex and errex.eid == entity.eid and self.name in errex.errors:
-            entity.req.data['displayederrors'].add(self.name)
-            return u'<span class="error">%s</span>' % errex.errors[self.name]
-        return u''
-
-    def render_help(self, entity):
-        """render a help message about the (edited) field"""
-        req = entity.req
-        help = [u'<div class="helper">']
-        descr = self.description or self.rschema.rproperty(self.subjtype, self.objtype, 'description')
-        if descr:
-            help.append(u'<span>%s</span>' % req._(descr))
-        example = self.render_example(req)
-        if example:
-            help.append(u'<span>(%s: %s)</span>'
-                        % (req._('sample format'), example))
-        help.append(u'</div>')
-        return u'&nbsp;'.join(help)
-
-    def render_example(self, req):
-        return u''
-
-    def render(self, entity):
-        """render the widget for a simple view"""
-        if not entity.has_eid():
-            return u''
-        return entity.printable_value(self.name)
-
-    def edit_render(self, entity, tabindex=None,
-                    includehelp=False, useid=None, **kwargs):
-        """render the widget for edition"""
-        # this is necessary to handle multiple edition
-        self.rname = eid_param(self.name, entity.eid)
-        if useid:
-            self.attrs['id'] = useid
-        elif self.autoid:
-            self.attrs['id'] = self.rname
-        if tabindex is not None:
-            self.attrs['tabindex'] = tabindex
-        else:
-            self.attrs['tabindex'] = entity.req.next_tabindex()
-        output = self._edit_render(entity, **kwargs)
-        if includehelp:
-            output += self.render_help(entity)
-        return output
-
-    def _edit_render(self, entity):
-        """do the actual job to render the widget for edition"""
-        raise NotImplementedError
-
-    def current_values(self, entity):
-        """return the value of the field associated to this widget on the given
-        entity. always return a list of values, which'll have size equal to 1
-        if the field is monovalued (like all attribute fields, but not all non
-        final relation fields
-        """
-        if self.rschema.is_final():
-            return entity.attribute_values(self.name)
-        elif entity.has_eid():
-            return [row[0] for row in entity.related(self.name, self.role)]
-        return ()
-
-    def current_value(self, entity):
-        return _value_from_values(self.current_values(entity))
-
-    def current_display_values(self, entity):
-        """same as .current_values but consider values stored in session in case
-        of validation error
-        """
-        values = entity.req.data.get('formvalues')
-        if values is None:
-            return self.current_values(entity)
-        cdvalues = values.get(self.rname)
-        if cdvalues is None:
-            return self.current_values(entity)
-        if not isinstance(cdvalues, (list, tuple)):
-            cdvalues = (cdvalues,)
-        return cdvalues
-
-    def current_display_value(self, entity):
-        """same as .current_value but consider values stored in session in case
-        of validation error
-        """
-        return _value_from_values(self.current_display_values(entity))
-
-    def hidden_input(self, entity, qvalue):
-        """return an hidden field which
-        1. indicates that a field is edited
-        2. hold the old value to easily detect if the field has been modified
-
-        `qvalue` is the html quoted old value
-        """
-        if self.role == 'subject':
-            editmark = 'edits'
-        else:
-            editmark = 'edito'
-        if qvalue is None or not entity.has_eid():
-            qvalue = INTERNAL_FIELD_VALUE
-        return u'<input type="hidden" name="%s-%s" value="%s"/>\n' % (
-            editmark, self.rname, qvalue)
-
-class InputWidget(Widget):
-    """abstract class for input generating a <input> tag"""
-    input_type = None
-    html_attributes = Widget.html_attributes | set(('type', 'name', 'value'))
-
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        return u'%s<input type="%s" name="%s" value="%s" %s/>' % (
-            self.hidden_input(entity, value), self.input_type,
-            self.rname, dvalue, self.format_attrs())
-
-class HiddenWidget(InputWidget):
-    input_type = 'hidden'
-    autoid = False
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 role='subject', **kwattrs):
-        InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
-                             role='subject',
-                             **kwattrs)
-        # disable access key
-        del self.attrs['accesskey']
-
-    def current_value(self, entity):
-        value = InputWidget.current_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def current_display_value(self, entity):
-        value = InputWidget.current_display_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def render_label(self, entity, label=None):
-        """render widget's label"""
-        return u''
-
-    def render_help(self, entity):
-        return u''
-
-    def hidden_input(self, entity, value):
-        """no hidden input for hidden input"""
-        return ''
-
-
-class EidWidget(HiddenWidget):
-
-    def _edit_render(self, entity):
-        return u'<input type="hidden" name="eid" value="%s" />' % entity.eid
-
-
-class StringWidget(InputWidget):
-    input_type = 'text'
-    html_attributes = InputWidget.html_attributes | set(('size', 'maxlength'))
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        attrs['size'] = min(maxsize, 40)
-        attrs['maxlength'] = maxsize
-
-
-class AutoCompletionWidget(StringWidget):
-    cubicwebns_attributes = (StringWidget.cubicwebns_attributes |
-                          set(('accesskey', 'size', 'maxlength')))
-    attrs = ()
-
-    wdgtype = 'SuggestField'
-
-    def current_value(self, entity):
-        value = StringWidget.current_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def _get_url(self, entity):
-        return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
-                                pageid=entity.req.pageid, mode='remote')
-
-    def _edit_render(self, entity):
-        req = entity.req
-        req.add_js( ('cubicweb.widgets.js', 'jquery.autocomplete.js') )
-        req.add_css('jquery.autocomplete.css')
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        iid = self.attrs.pop('id')
-        if self.required(entity):
-            cssclass = u' required'
-        else:
-            cssclass = u''
-        dataurl = self._get_url(entity)
-        return (u'%(hidden)s<input type="text" name="%(iid)s" value="%(value)s" cubicweb:dataurl="%(url)s" class="widget%(required)s" id="%(iid)s" '
-                u'tabindex="%(tabindex)s" cubicweb:loadtype="auto" cubicweb:wdgtype="%(wdgtype)s"  %(attrs)s />' % {
-                    'iid': iid,
-                    'hidden': self.hidden_input(entity, value),
-                    'wdgtype': self.wdgtype,
-                    'url': xml_escape(dataurl),
-                    'tabindex': self.attrs.pop('tabindex'),
-                    'value': dvalue,
-                    'attrs': self.format_attrs(),
-                    'required' : cssclass,
-                    })
-
-class StaticFileAutoCompletionWidget(AutoCompletionWidget):
-    wdgtype = 'StaticFileSuggestField'
-
-    def _get_url(self, entity):
-        return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
-
-class RestrictedAutoCompletionWidget(AutoCompletionWidget):
-    wdgtype = 'RestrictedSuggestField'
-
-
-class PasswordWidget(InputWidget):
-    input_type = 'password'
-
-    def required(self, entity):
-        if InputWidget.required(self, entity) and not entity.has_eid():
-            return True
-        return False
-
-    def current_values(self, entity):
-        # on existant entity, show password field has non empty (we don't have
-        # the actual value
-        if entity.has_eid():
-            return (INTERNAL_FIELD_VALUE,)
-        return super(PasswordWidget, self).current_values(entity)
-
-    def _edit_render(self, entity):
-        html = super(PasswordWidget, self)._edit_render(entity)
-        name = eid_param(self.name + '-confirm', entity.eid)
-        return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/>&nbsp;<span class="emphasis">(%s)</span>' % (
-            html, self.input_type, name, name, entity.req.next_tabindex(),
-            entity.req._('confirm password'))
-
-
-class TextWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('rows', 'cols'))
-
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        if 256 < maxsize < 513:
-            attrs['cols'], attrs['rows'] = 60, 5
-        else:
-            attrs['cols'], attrs['rows'] = 80, 10
-
-    def render(self, entity):
-        if not entity.has_eid():
-            return u''
-        return entity.printable_value(self.name)
-
-    def _edit_render(self, entity, with_format=True):
-        req = entity.req
-        editor = self._edit_render_textarea(entity, with_format)
-        value = self.current_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        return u'%s%s' % (self.hidden_input(entity, value), editor)
-
-    def _edit_render_textarea(self, entity, with_format):
-        self.attrs.setdefault('cols', 80)
-        self.attrs.setdefault('rows', 20)
-        dvalue = self.current_display_value(entity)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        if entity.use_fckeditor(self.name):
-            entity.req.fckeditor_config()
-            if with_format:
-                if entity.has_eid():
-                    format = entity.attr_metadata(self.name, 'format')
-                else:
-                    format = ''
-                frname = eid_param(self.name + '_format', entity.eid)
-                hidden = u'<input type="hidden" name="edits-%s" value="%s"/>\n'\
-                         '<input type="hidden" name="%s" value="text/html"/>\n' % (
-                    frname, format, frname)
-            return u'%s<textarea cubicweb:type="wysiwyg" onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
-                hidden, self.rname, self.format_attrs(), dvalue)
-        if with_format and entity.e_schema.has_metadata(self.name, 'format'):
-            fmtwdg = entity.get_widget(self.name + '_format')
-            fmtwdgstr = fmtwdg.edit_render(entity, tabindex=self.attrs['tabindex'])
-            self.attrs['tabindex'] = entity.req.next_tabindex()
-        else:
-            fmtwdgstr = ''
-        return u'%s<br/><textarea onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
-            fmtwdgstr, self.rname, self.format_attrs(), dvalue)
-
-
-class CheckBoxWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('checked', ))
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        return self.hidden_input(entity, value) + checkbox(self.rname, 'checked', self.format_attrs(), dvalue)
-
-    def render(self, entity):
-        if not entity.has_eid():
-            return u''
-        if getattr(entity, self.name):
-            return entity.req._('yes')
-        return entity.req._('no')
-
-
-class YesNoRadioWidget(CheckBoxWidget):
-    html_attributes = Widget.html_attributes | set(('disabled',))
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        attrs1 = self.format_attrs()
-        del self.attrs['id'] # avoid duplicate id for xhtml compliance
-        attrs2 = self.format_attrs()
-        if dvalue:
-            attrs1 += ' checked="checked"'
-        else:
-            attrs2 += ' checked="checked"'
-        wdgs = [self.hidden_input(entity, value),
-                u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')),
-                u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
-        return '\n'.join(wdgs)
-
-
-class FileWidget(Widget):
-    need_multipart = True
-    def _file_wdg(self, entity):
-        wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())]
-        req = entity.req
-        if (entity.e_schema.has_metadata(self.name, 'format')
-            or entity.e_schema.has_metadata(self.name, 'encoding')):
-            divid = '%s-%s-advanced' % (self.name, entity.eid)
-            wdgs.append(u'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>' %
-                        (xml_escape(toggle_action(divid)),
-                         req._('show advanced fields'),
-                         xml_escape(req.build_url('data/puce_down.png')),
-                         req._('show advanced fields')))
-            wdgs.append(u'<div id="%s" class="hidden">' % divid)
-            for extraattr in ('_format', '_encoding'):
-                if entity.e_schema.has_subject_relation('%s%s' % (self.name, extraattr)):
-                    ewdg = entity.get_widget(self.name + extraattr)
-                    wdgs.append(ewdg.render_label(entity))
-                    wdgs.append(ewdg.edit_render(entity, includehelp=True))
-                    wdgs.append(u'<br/>')
-            wdgs.append(u'</div>')
-        if entity.has_eid():
-            if not self.required(entity):
-                # trick to be able to delete an uploaded file
-                wdgs.append(u'<br/>')
-                wdgs.append(checkbox(eid_param('__%s_detach' % self.rname, entity.eid), False))
-                wdgs.append(req._('detach attached file %s' % entity.dc_title()))
-            else:
-                wdgs.append(u'<br/>')
-                wdgs.append(req._('currently attached file: %s' % entity.dc_title()))
-        return '\n'.join(wdgs)
-
-    def _edit_render(self, entity):
-        return self.hidden_input(entity, None) + self._file_wdg(entity)
-
-
-class TextFileWidget(FileWidget):
-    def _edit_msg(self, entity):
-        if entity.has_eid() and not self.required(entity):
-            msg = entity.req._(
-                '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.')
-        else:
-            msg = entity.req._(
-                'You can either submit a new file using the browse button above'
-                ', or edit file content online with the widget below.')
-        return msg
-
-    def _edit_render(self, entity):
-        wdgs = [self._file_wdg(entity)]
-        if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'):
-            msg = self._edit_msg(entity)
-            wdgs.append(u'<p><b>%s</b></p>' % msg)
-            twdg = TextWidget(self.vreg, self.subjtype, self.rschema, self.objtype)
-            twdg.rname = self.rname
-            data = getattr(entity, self.name)
-            if data:
-                encoding = entity.attr_metadata(self.name, 'encoding')
-                try:
-                    entity[self.name] = unicode(data.getvalue(), encoding)
-                except UnicodeError:
-                    pass
-                else:
-                    wdgs.append(twdg.edit_render(entity, with_format=False))
-                    entity[self.name] = data # restore Binary value
-            wdgs.append(u'<br/>')
-        return '\n'.join(wdgs)
-
-
-class ComboBoxWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('multiple', 'size'))
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 multiple=False, **kwattrs):
-        super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
-                                             **kwattrs)
-        if multiple:
-            self.attrs['multiple'] = 'multiple'
-            if not 'size' in self.attrs:
-                self.attrs['size'] = '5'
-        # disable access key (dunno why but this is not allowed by xhtml 1.0)
-        del self.attrs['accesskey']
-
-    def vocabulary(self, entity):
-        raise NotImplementedError()
-
-    def form_value(self, entity, value, values):
-        if value in values:
-            flag = 'selected="selected"'
-        else:
-            flag = ''
-        return value, flag
-
-    def _edit_render(self, entity):
-        values = self.current_values(entity)
-        if values:
-            res = [self.hidden_input(entity, v) for v in values]
-        else:
-            res = [self.hidden_input(entity, INTERNAL_FIELD_VALUE)]
-        dvalues = self.current_display_values(entity)
-        res.append(u'<select name="%s" %s>' % (self.rname, self.format_attrs()))
-        for label, value in self.vocabulary(entity):
-            if value is None:
-                # handle separator
-                res.append(u'<optgroup label="%s"/>' % (label or ''))
-            else:
-                value, flag = self.form_value(entity, value, dvalues)
-                res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
-        res.append(u'</select>')
-        return '\n'.join(res)
-
-
-class StaticComboBoxWidget(ComboBoxWidget):
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 vocabfunc, multiple=False, sort=False, **kwattrs):
-        super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
-                                                   multiple, **kwattrs)
-        self.sort = sort
-        self.vocabfunc = vocabfunc
-
-    def vocabulary(self, entity):
-        choices = self.vocabfunc(entity=entity)
-        if self.sort:
-            choices = sorted(choices)
-        if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
-            return zip((entity.req._(v) for v in choices), choices)
-        return zip(choices, choices)
-
-
-class EntityLinkComboBoxWidget(ComboBoxWidget):
-    """to be used be specific forms"""
-
-    def current_values(self, entity):
-        if entity.has_eid():
-            return [r[0] for r in entity.related(self.name, self.role)]
-        defaultmeth = 'default_%s_%s' % (self.role, self.name)
-        if hasattr(entity, defaultmeth):
-            return getattr(entity, defaultmeth)()
-        return ()
-
-    def vocabulary(self, entity):
-        return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role)
-
-
-class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget):
-
-    def vocabulary(self, entity, limit=None):
-        req = entity.req
-        # first see if its specified by __linkto form parameters
-        linkedto = entity.linked_to(self.name, self.role)
-        if linkedto:
-            entities = (req.entity_from_eid(eid) for eid in linkedto)
-            return [(entity.view('combobox'), entity.eid) for entity in entities]
-        # it isn't, check if the entity provides a method to get correct values
-        if not self.required(entity):
-            res = [('', INTERNAL_FIELD_VALUE)]
-        else:
-            res = []
-        # vocabulary doesn't include current values, add them
-        if entity.has_eid():
-            rset = entity.related(self.name, self.role)
-            relatedvocab = [(e.view('combobox'), e.eid) for e in rset.entities()]
-        else:
-            relatedvocab = []
-        return res + entity.vocabulary(self.rschema, self.role) + relatedvocab
-
-
-class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
-
-    def vocabulary(self, entity, limit=None):
-        return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
-
-
-class AddComboBoxWidget(DynamicComboBoxWidget):
-    def _edit_render(self, entity):
-        req = entity.req
-        req.add_js( ('cubicweb.ajax.js', 'jquery.js', 'cubicweb.widgets.js') )
-        values = self.current_values(entity)
-        if values:
-            res = [self.hidden_input(entity, v) for v in values]
-        else:
-            res = [self.hidden_input(entity, INTERNAL_FIELD_VALUE)]
-        dvalues = self.current_display_values(entity)
-        etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]
-        res.append(u'<select class="widget" cubicweb:etype_to="%s" cubicweb:etype_from="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="AddComboBox" name="%s" %s>'
-                   % (entity.e_schema, etype_from, self.rname, self.format_attrs()))
-        for label, value in self.vocabulary(entity):
-            if value is None:
-                # handle separator
-                res.append(u'<optgroup label="%s"/>' % (label or ''))
-            else:
-                value, flag = self.form_value(entity, value, dvalues)
-                res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
-        res.append(u'</select>')
-        res.append(u'<div id="newvalue">')
-        res.append(u'<input type="text" id="newopt" />')
-        res.append(u'<a href="javascript:noop()" id="add_newopt">&nbsp;</a></div>')
-        return '\n'.join(res)
-
-
-class IntegerWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        return '23'
-
-
-class FloatWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        formatstr = req.property_value('ui.float-format')
-        return formatstr % 1.23
-
-    def current_values(self, entity):
-        values = entity.attribute_values(self.name)
-        if values:
-            formatstr = entity.req.property_value('ui.float-format')
-            value = values[0]
-            if value is not None:
-                value = float(value)
-            else:
-                return ()
-            return [formatstr % value]
-        return ()
-
-
-class DecimalWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        return '345.0300'
-
-
-class DateWidget(StringWidget):
-    format_key = 'ui.date-format'
-    monthnames = ('january', 'february', 'march', 'april',
-                  'may', 'june', 'july', 'august',
-                  'september', 'october', 'november', 'december')
-    daynames = ('monday', 'tuesday', 'wednesday', 'thursday',
-                'friday', 'saturday', 'sunday')
-
-    @classmethod
-    def add_localized_infos(cls, req):
-        """inserts JS variables defining localized months and days"""
-        # import here to avoid dependancy from cubicweb-common to simplejson
-        _ = req._
-        monthnames = [_(mname) for mname in cls.monthnames]
-        daynames = [_(dname) for dname in cls.daynames]
-        req.html_headers.define_var('MONTHNAMES', monthnames)
-        req.html_headers.define_var('DAYNAMES', daynames)
-
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs.setdefault('size', 10)
-        kwattrs.setdefault('maxlength', 10)
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def current_values(self, entity):
-        values = entity.attribute_values(self.name)
-        if values and hasattr(values[0], 'strftime'):
-            formatstr = entity.req.property_value(self.format_key)
-            return [values[0].strftime(str(formatstr))]
-        return values
-
-    def render_example(self, req):
-        formatstr = req.property_value(self.format_key)
-        return datetime.now().strftime(str(formatstr))
-
-
-    def _edit_render(self, entity):
-        wdg = super(DateWidget, self)._edit_render(entity)
-        cal_button = self.render_calendar_popup(entity)
-        return wdg+cal_button
-
-    def render_help(self, entity):
-        """calendar popup widget"""
-        req = entity.req
-        help = [ u'<div class="helper">' ]
-        descr = self.rschema.rproperty(self.subjtype, self.objtype, 'description')
-        if descr:
-            help.append('<span>%s</span>' % req._(descr))
-        example = self.render_example(req)
-        if example:
-            help.append('<span>(%s: %s)</span>'
-                        % (req._('sample format'), example))
-        help.append(u'</div>')
-        return u'&nbsp;'.join(help)
-
-    def render_calendar_popup(self, entity):
-        """calendar popup widget"""
-        req = entity.req
-        self.add_localized_infos(req)
-        req.add_js(('cubicweb.ajax.js', 'cubicweb.calendar.js',))
-        req.add_css(('cubicweb.calendar_popup.css',))
-        inputid = self.attrs.get('id', self.rname)
-        helperid = "%shelper" % inputid
-        _today = datetime.now()
-        year = int(req.form.get('year', _today.year))
-        month = int(req.form.get('month', _today.month))
-
-        return (u"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper">
-<img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
-                % (helperid, inputid, year, month,
-                   req.external_resource('CALENDAR_ICON'), req._('calendar'), helperid) )
-
-class DateTimeWidget(DateWidget):
-    format_key = 'ui.datetime-format'
-
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 16
-        kwattrs['maxlength'] = 16
-        DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        formatstr1 = req.property_value('ui.datetime-format')
-        formatstr2 = req.property_value('ui.date-format')
-        return req._('%(fmt1)s, or without time: %(fmt2)s') % {
-            'fmt1': datetime.now().strftime(str(formatstr1)),
-            'fmt2': datetime.now().strftime(str(formatstr2)),
-            }
-
-
-class TimeWidget(StringWidget):
-    format_key = 'ui.time-format'
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 5
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-
-class EmailWidget(StringWidget):
-
-    def render(self, entity):
-        email = getattr(entity, self.name)
-        if not email:
-            return u''
-        return u'<a href="mailto:%s">%s</a>' % (email, email)
-
-class URLWidget(StringWidget):
-
-    def render(self, entity):
-        url = getattr(entity, self.name)
-        if not url:
-            return u''
-        url = xml_escape(url)
-        return u'<a href="%s">%s</a>' % (url, url)
-
-class EmbededURLWidget(StringWidget):
-
-    def render(self, entity):
-        url = getattr(entity, self.name)
-        if not url:
-            return u''
-        aurl = xml_escape(entity.build_url('embed', url=url))
-        return u'<a href="%s">%s</a>' % (aurl, url)
-
-
-
-def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
-                   **kwargs):
-    """return the most adapated widget to edit the relation
-    'subjschema rschema objschema' according to information found in the schema
-    """
-    if role == 'subject':
-        eclass, subjschema = _eclass_eschema(subjschema)
-    else:
-        eclass, objschema = _eclass_eschema(objschema)
-    if eclass is not None and rschema in getattr(eclass, 'widgets', ()):
-        wcls = WIDGETS[eclass.widgets[rschema]]
-    elif not rschema.is_final():
-        card = rschema.rproperty(subjschema, objschema, 'cardinality')
-        if role == 'object':
-            multiple = card[1] in '+*'
-        else: #if role == 'subject':
-            multiple = card[0] in '+*'
-        return DynamicComboBoxWidget(vreg, subjschema, rschema, objschema,
-                                     role=role, multiple=multiple)
-    else:
-        wcls = None
-    factory = FACTORIES.get(objschema, _default_widget_factory)
-    return factory(vreg, subjschema, rschema, objschema, wcls=wcls,
-                   role=role, **kwargs)
-
-
-# factories to find the most adapated widget according to a type and other constraints
-
-def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
-    w = None
-    for c in rschema.rproperty(subjschema, objschema, 'constraints'):
-        if isinstance(c, StaticVocabularyConstraint):
-            # may have been set by a previous SizeConstraint but doesn't make sense
-            # here (even doesn't have the same meaning on a combobox actually)
-            kwargs.pop('size', None)
-            return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema,
-                                                  vocabfunc=c.vocabulary, **kwargs)
-        if isinstance(c, SizeConstraint) and c.max is not None:
-            # don't return here since a StaticVocabularyConstraint may
-            # follow
-            if wcls is None:
-                if c.max < 257:
-                    _wcls = StringWidget
-                else:
-                    _wcls = TextWidget
-            else:
-                _wcls = wcls
-            _wcls.size_constraint_attrs(kwargs, c.max)
-            w = _wcls(vreg, subjschema, rschema, objschema, **kwargs)
-    if w is None:
-        w = (wcls or TextWidget)(vreg, subjschema, rschema, objschema, **kwargs)
-    return w
-
-def _default_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
-    if wcls is None:
-        wcls = _WFACTORIES[objschema]
-    return wcls(vreg, subjschema, rschema, objschema, **kwargs)
-
-FACTORIES = {
-    'String' :  _string_widget_factory,
-    'Boolean':  _default_widget_factory,
-    'Bytes':    _default_widget_factory,
-    'Date':     _default_widget_factory,
-    'Datetime': _default_widget_factory,
-    'Float':    _default_widget_factory,
-    'Decimal':    _default_widget_factory,
-    'Int':      _default_widget_factory,
-    'Password': _default_widget_factory,
-    'Time':     _default_widget_factory,
-    }
-
-# default widget by entity's type
-_WFACTORIES = {
-    'Boolean':  YesNoRadioWidget,
-    'Bytes':    FileWidget,
-    'Date':     DateWidget,
-    'Datetime': DateTimeWidget,
-    'Int':      IntegerWidget,
-    'Float':    FloatWidget,
-    'Decimal':  DecimalWidget,
-    'Password': PasswordWidget,
-    'String' :  StringWidget,
-    'Time':     TimeWidget,
-    }
-
-# widgets registry
-WIDGETS = {}
-def register(widget_list):
-    for obj in widget_list:
-        if isinstance(obj, type) and issubclass(obj, Widget):
-            if obj is Widget or obj is ComboBoxWidget:
-                continue
-            WIDGETS[obj.__name__] = obj
-
-register(globals().values())
--- a/wsgi/handler.py	Fri Aug 14 00:01:12 2009 +0200
+++ b/wsgi/handler.py	Fri Aug 14 00:02:08 2009 +0200
@@ -97,7 +97,7 @@
 #         assert self.base_url[-1] == '/'
 #         self.https_url = config['https-url']
 #         assert not self.https_url or self.https_url[-1] == '/'
-        self.url_rewriter = self.appli.vreg.select_object('components', 'urlrewriter')
+        self.url_rewriter = self.appli.vreg['components'].select_or_none('urlrewriter')
 
     def _render(self, req):
         """this function performs the actual rendering